Railway Operation Simulator  v2.6.1
A railway simulator for Windows
TrainUnit.cpp
Go to the documentation of this file.
1 // TrainUnit.cpp
2 /*
3  BEWARE OF COMMENTS in .cpp files: they were accurate when written but have
4  sometimes been overtaken by changes and not updated
5  Comments in .h files are believed to be accurate and up to date
6 
7  This is a source code file for "railway.exe", a railway operation
8  simulator, written originally in Borland C++ Builder 4 Professional with
9  later updates in Embarcadero C++Builder 10.2.
10  Copyright (C) 2010 Albert Ball [original development]
11 
12  This program is free software: you can redistribute it and/or modify
13  it under the terms of the GNU General Public License as published by
14  the Free Software Foundation, either version 3 of the License, or
15  (at your option) any later version.
16 
17  This program is distributed in the hope that it will be useful,
18  but WITHOUT ANY WARRANTY; without even the implied warranty of
19  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  GNU General Public License for more details.
21 
22  You should have received a copy of the GNU General Public License
23  along with this program. If not, see <http://www.gnu.org/licenses/>.
24 */
25 // ---------------------------------------------------------------------------
26 #include <Classes.hpp>
27 #include <Controls.hpp>
28 #include <StdCtrls.hpp>
29 #include <Forms.hpp>
30 #include <Buttons.hpp>
31 #include <ExtCtrls.hpp>
32 #include <Menus.hpp>
33 #include <Dialogs.hpp>
34 #include <Graphics.hpp>
35 #include <ComCtrls.hpp>
36 #include <fstream>
37 #include <vector>
38 #include <algorithm> //for sort
39 #include <vcl.h>
40 #include <stdlib.h> //for rand()
41 #include <math.hpp> //for speed & performance calcs
42 
43 #pragma hdrstop
44 
45 #include "TrainUnit.h"
46 #include "TrackUnit.h"
47 #include "GraphicUnit.h"
48 #include "DisplayUnit.h"
49 #include "Utilities.h"
50 
51 // ---------------------------------------------------------------------------
52 #pragma package(smart_init)
53 
55 
56 // ---------------------------------------------------------------------------
57 
58 int TTrain::NextTrainID = 0; // has to be initialised outside the class
59 
60 // ---------------------------------------------------------------------------
61 
62 TTrain::TTrain(int Caller, int RearStartElementIn, int RearStartExitPosIn, AnsiString InputCode, int StartSpeedIn, int MassIn, double MaxRunningSpeedIn,
63  double MaxBrakeRateIn, double PowerAtRailIn, TTrainMode TrainModeIn, TTrainDataEntry *TrainDataEntryPtrIn, int RepeatNumberIn, int IncrementalMinutesIn,
64  int IncrementalDigitsIn, int SignallerMaxSpeedIn): RearStartElement(RearStartElementIn), RearStartExitPos(RearStartExitPosIn), HeadCode(InputCode),
65  StartSpeed(StartSpeedIn), Mass(MassIn), MaxRunningSpeed(MaxRunningSpeedIn), MaxBrakeRate(MaxBrakeRateIn), PowerAtRail(PowerAtRailIn),
66  TrainMode(TrainModeIn), TrainDataEntryPtr(TrainDataEntryPtrIn), RepeatNumber(RepeatNumberIn), IncrementalMinutes(IncrementalMinutesIn),
67  IncrementalDigits(IncrementalDigitsIn), SignallerMaxSpeed(SignallerMaxSpeedIn)
68 /*
69  Construct a new train with general default values and input values for position and headcode.
70  Create the frontcode, headcode and background graphics here but don't delete them in a destructor.
71  This is because trains are kept in a vector and vectors erase elements during internal operations.
72  Deletion is explicit by using a special function. Increment the static class member NextTrainID
73  after setting this train's ID.
74 */
75 
76 {
77  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TTrain," + AnsiString(RearStartElementIn) + "," +
78  AnsiString(RearStartExitPosIn) + "," + AnsiString(InputCode) + "," + AnsiString(StartSpeedIn) + "," + AnsiString(MassIn) + "," +
79  AnsiString(TrainModeIn));
80  // AutoControl = true;//all trains start in auto control
81  UpdateCounter = 0;
82  TimeTimeLocArrived = false;
83  Derailed = false;
84  DerailPending = false;
85  Crashed = false;
86  StoppedAtBuffers = false;
87  StoppedAtSignal = false;
88  StoppedAtLocation = false;
89  StoppedAfterSPAD = false;
90  StoppedWithoutPower = false; // new at v2.4.0
91  StoppedForTrainInFront = false;
92  SignallerStoppingFlag = false;
93  SignallerStopped = false;
94  SignallerRemoved = false;
95  NotInService = false;
96  HoldAtLocationInTTMode = false;
97  AllowedToPassRedSignal = false;
98  CallingOnFlag = false;
99  BeingCalledOn = false;
100  DepartureTimeSet = false;
102  TimetableFinished = false;
103  LastActionDelayFlag = false;
104  OneLengthAccelDecel = false;
105  TrainCrashedInto = -1;
107  Plotted = false;
108  TrainGone = false;
109  SPADFlag = false;
110  FrontCodePtr = new Graphics::TBitmap;
111  FrontCodePtr->PixelFormat = pf8bit;
112  FrontCodePtr->Height = 8;
113  FrontCodePtr->Width = 8;
115  FrontCodePtr->Transparent = false;
116  AValue = sqrt(2 * PowerAtRail / Mass);
118  TerminatedMessageSent = false;
119  JoinedOtherTrainFlag = false;
121  StepForwardFlag = false;
123  for(int x = 0; x < 4; x++)
124  {
125  HeadCodeGrPtr[x] = new Graphics::TBitmap;
126  HeadCodeGrPtr[x]->PixelFormat = pf8bit;
127  HeadCodeGrPtr[x]->Height = 8;
128  HeadCodeGrPtr[x]->Width = 8;
130  HeadCodeGrPtr[x]->Transparent = false;
131  }
132  for(int x = 0; x < 4; x++)
133  {
134  BackgroundPtr[x] = new Graphics::TBitmap;
135  BackgroundPtr[x]->PixelFormat = pf8bit;
136  BackgroundPtr[x]->Height = 8;
137  BackgroundPtr[x]->Width = 8;
139  BackgroundPtr[x]->Transparent = false;
140  }
141  for(int x = 0; x < 4; x++)
142  {
144  // set here to ensure have values
145  }
146  for(int x = 0; x < 4; x++)
147  {
148  PlotElement[x] = -1; // marker for not plotted yet
149  }
150  for(int x = 0; x < 3; x++)
151  {
152  OldZoomOutElement[x] = -1; // marker for not plotted yet
153  }
155  NextTrainID++;
156 
157  // new values added to complete initialisation of all TTrain variables
158 
159  // ActionVectorEntryPtr = &(TrainDataEntryPtr->ActionVector.at(0)); can't be initialised yet as session trains created with Null
160  // TrainDataEntryPtr, initialise in AddTrain
162  FrontElementLength = 0;
163  EntrySpeed = 0;
164  ExitSpeedHalf = 0;
165  ExitSpeedFull = 0;
166  MaxExitSpeed = 0;
167  BrakeRate = 0;
169  FirstHalfMove = true;
170  EntryTime = 0;
171  ExitTimeHalf = 0;
172  ExitTimeFull = 0;
173  ReleaseTime = 0;
174  TRSTime = 0;
175  LastActionTime = 0;
176  Straddle = MidLag;
177  LeadElement = -1;
178  LeadEntryPos = 0;
179  LeadExitPos = 0;
180  MidElement = -1;
181  MidEntryPos = 0;
182  MidExitPos = 0;
183  LagElement = -1;
184  LagEntryPos = 0;
185  LagExitPos = 0;
186  TrainFailed = false; // added at v2.4.0
187  for(int x = 0; x < 4; x++)
188  {
189  HOffset[x] = 0;
190  VOffset[x] = 0;
191  PlotEntryPos[x] = 0;
192  }
193  OpTimeToAct = 60; // default value, new at v2.2.0
194  MinsDelayed = 0.0; // new at v2.2.0
195  FirstLaterStopRecoverableTime = 0.0; // new at v2.2.0
196  FinishJoinLogSent = false;
197  // added at v2.4.0 to prevent repeatdly logging the event
200  // added at v2.4.0, no need to include in session file as will only be sent once & better that way
204  ZeroPowerNoCDTMessage = false;
209  TrainFailurePending = false;
210  Utilities->CallLogPop(648);
211 }
212 
213 // ---------------------------------------------------------------------------
214 
215 void TTrain::DeleteTrain(int Caller)
216 /*
217  Delete train heap objects (bitmaps) explicitly by this special function rather than by a destructor, because vectors
218  erase elements during internal operations & if TTrain had an explicit destructor that deleted the heap elements then
219  it would be called when a vector element was erased. Calling the default TTrain destructor doesn't matter because all that
220  does is release the memory of the members (including pointers to the bitmaps), it doesn't destroy the bitmaps themselves.
221  It's important therefore to call this function before erasing the vector element, otherwise the pointers to the bitmaps
222  would be lost and the bitmaps never destroyed, thereby causing memory leaks.
223  No need to delete HeadCodePosition as that just points to existing bitmaps
224 */ {
225  // if(NoDelete) return;//used when a TTrain is created to hold copied values from elsewhere
226  TrainController->LogEvent("" + AnsiString(Caller) + ",DeleteTrain," + HeadCode);
227  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",DeleteTrain," + HeadCode);
228  if(Display->ZoomOutFlag)
230  if(FrontCodePtr == 0)
231  {
232  throw Exception("Error in attempting to delete FrontCodePtr");
233  }
234  delete FrontCodePtr;
235  FrontCodePtr = 0;
236  for(int x = 0; x < 4; x++)
237  {
238  if(BackgroundPtr[x] == 0)
239  {
240  throw Exception("Error in attempting to delete BackgroundPtr[" + AnsiString(x) + "]");
241  }
242  delete BackgroundPtr[x];
243  BackgroundPtr[x] = 0;
244  }
245  for(int x = 0; x < 4; x++)
246  {
247  if(HeadCodeGrPtr[x] == 0)
248  {
249  throw Exception("Error in attempting to delete HeadCodeGrPtr[" + AnsiString(x) + "]");
250  }
251  delete HeadCodeGrPtr[x];
252  HeadCodeGrPtr[x] = 0;
253  }
254  Utilities->CallLogPop(649);
255 }
256 
257 // ---------------------------------------------------------------------------
258 
260 /*
261  Plots the train starting position on screen. Note that the check for starting on straight points &
262  on wrongly set points is carried out in TrainControllerUnit [but have to allow for starting on points because
263  ChangeDirection calls this function.]. Train starts on Lead & Mid elements & Straddle = LeadMid unless
264  entering at a continuation in which case Straddle = MidLag & train not plotted immediately.
265  Set the headcode graphics pointers from the headcode text, then check whether starting at a
266  continuation. If so set Mid & Lag elements to -1 so they won't be plotted, and set Lead values
267  for the continuation element. Otherwise set Lead and Mid values,
268 
269  and Lead element value unless
270  Mid element is a buffer or continuation. Set Straddle, then for the Mid element set the graphic
271  offsets and headcode positions and front code. Pick up background bitmaps for the Mid element,
272  then check if a train on either Mid or Lag and if so give a warning message and return false so
273  that the calling function can delete the train. Plot the Mid element train values then do similarly
274  for the Lag element - set offsets, pick up background bitmaps, and plot the rear two segments of
275  the train. Finally set the Plotted flag and return true.
276 */ {
277  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotStartPosition," + HeadCode);
278  int NextElementPosition, NextEntryPos, ElementLength, SpeedLimit;
279 
281  // PlotStartTime = TrainController->TTClockTime;
282  FirstHalfMove = true;
283 
284  // if enter at continuation then don't plot anything at start, but set TrainIDOnElement for continuation entry so as to
285  // 'claim' it for this train to prevent any other waiting trains trying to enter
287  {
288  LagElement = -1; // not to be plotted
289  LagExitPos = 0; // not to be plotted
290  LagEntryPos = 0; // not to be plotted
291  MidElement = -1; // not to be plotted
292  MidExitPos = 0; // not to be plotted
293  MidEntryPos = 0; // not to be plotted
295  LeadExitPos = 1; // will be 1 for continuation entry
296  LeadEntryPos = 0;
297 
299  MaxExitSpeed = StartSpeed; // initial value
301  ElementLength = Track->TrackElementAt(164, LeadElement).Length01;
302  SpeedLimit = Track->TrackElementAt(165, LeadElement).SpeedLimit01;
303  if(EntrySpeed > SpeedLimit)
304  EntrySpeed = SpeedLimit;
308  // LeadElement is the element to be entered
309 
310  // Precautionary check - If need to brake EntrySpeed may be too high, so set it to the speed at which
311  // can achieve ExitSpeedFull at the half braking rate.
313  {
314  double TempEntrySpeed = sqrt((MaxExitSpeed * MaxExitSpeed) + (3.6 * 3.6 * MaxBrakeRate * ElementLength)); // half braking
315  if(TempEntrySpeed < EntrySpeed)
316  {
317  EntrySpeed = TempEntrySpeed;
319  }
320  }
321  Straddle = MidLag; // only for starting on a continuation
323  // no need to stop gap flashing if start on continuation
324  }
325  else // not starting at a continuation
326  {
327  LagElement = -1;
328  LagEntryPos = 0;
329  LagExitPos = 0;
336 
338  MaxExitSpeed = StartSpeed; // initial value
340  bool TempDerail = false; // dummy
341  NextElementPosition = Track->TrackElementAt(168, LeadElement).Conn[Track->GetAnyElementOppositeLinkPos(2, LeadElement, LeadEntryPos, TempDerail)];
343  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
344  {
345  StoppedWithoutPower = true;
346  }
347  // facing buffers check - ignore starting speed if start facing buffers
348  StoppedAtBuffers = false;
349  // need to set here as well as in UpdateTrain() in case paused during signaller change direction
352  {
353  FrontElementSpeedLimit = Track->TrackElementAt(494, LeadElement).SpeedLimit01; // use 01 for convenience, not used
354  FrontElementLength = Track->TrackElementAt(495, LeadElement).Length01; // use 01 for convenience, not used
355  EntrySpeed = 0;
356  ExitSpeedHalf = 0;
357  ExitSpeedFull = 0;
358  MaxExitSpeed = 0;
359  // SetTrainMovementValues not called so set this here
360  BrakeRate = 0;
363  StoppedAtSignal = false;
364  // new v2.2.0: can't be at buffers and signal! If was set then won't be reset as later
365  // signal check is an 'else'
366  if(!StoppedAtLocation)
367  StoppedAtBuffers = true; // stopped at location takes precedence
368  }
369 
370  // facing continuation check - don't allow to stop even if no power
372  {
373  FrontElementSpeedLimit = Track->TrackElementAt(509, LeadElement).SpeedLimit01; // use 01 for convenience, not used
374  FrontElementLength = Track->TrackElementAt(510, LeadElement).Length01; // use 01 for convenience, not used
378  BrakeRate = 0;
379  ExitTimeHalf = TrainController->TTClockTime + TDateTime(1.8 * (double) FrontElementLength / EntrySpeed / 86400);
380  ExitTimeFull = TrainController->TTClockTime + TDateTime(3.6 * (double) FrontElementLength / EntrySpeed / 86400);
381  }
382 
383  // Signal check
384  else if((NextElementPosition > -1) && (NextEntryPos > -1))
385  // condition check added as precaution after SloughIECC error reported by James U
386  {
387  if((Track->TrackElementAt(170, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
388  (Track->TrackElementAt(171, NextElementPosition).Attribute == 0) && !StoppedWithoutPower)
389  {
390  FrontElementSpeedLimit = Track->TrackElementAt(172, LeadElement).SpeedLimit01; // use 01 for convenience, not used
391  FrontElementLength = Track->TrackElementAt(173, LeadElement).Length01; // use 01 for convenience, not used
392  EntrySpeed = 0;
393  ExitSpeedHalf = 0;
394  ExitSpeedFull = 0;
395  MaxExitSpeed = 0;
396  BrakeRate = 0;
399  if(!StoppedAtLocation) //if it is stopped at location then don't want StoppedAtSignal until departure time if still red then, & UpdateTrain takes care of thet
400  {
401  StoppedAtSignal = true;
403  // TrainController->LogActionError(39, HeadCode, "", SignalHold, Track->TrackElementAt(754, NextElementPosition).ElementID);
404  }
406  { // set both StoppedAtLocation & StoppedAtSignal, so that 'pass red signal' is offered in popup menu rather than move
407  // forwards, but don't change the background colour so still shows as stopped at location
408  StoppedAtSignal = true;
409  }
410  }
411  else
412  {
413  StoppedAtSignal = false;
414  if(NextEntryPos > 1)
415  {
416  ElementLength = Track->TrackElementAt(174, NextElementPosition).Length23;
417  SpeedLimit = Track->TrackElementAt(175, NextElementPosition).SpeedLimit23;
418  }
419  else
420  {
421  ElementLength = Track->TrackElementAt(176, NextElementPosition).Length01;
422  SpeedLimit = Track->TrackElementAt(177, NextElementPosition).SpeedLimit01;
423  }
424  if(EntrySpeed > SpeedLimit)
425  EntrySpeed = SpeedLimit;
429  TDateTime TestTime = TrainController->TTClockTime; // test
430  AnsiString TimeString = Utilities->Format96HHMMSS(TestTime); // test
431  SetTrainMovementValues(2, NextElementPosition, NextEntryPos);
432  // NextElement is the element to be entered
433 
434  // Precautionary check - If need to brake EntrySpeed may be too high, so set it to the speed at which
435  // can achieve ExitSpeedFull at the half braking rate.
437  {
438  double TempEntrySpeed = sqrt((MaxExitSpeed * MaxExitSpeed) + (3.6 * 3.6 * MaxBrakeRate * ElementLength));
439  // half braking
440  if(TempEntrySpeed < EntrySpeed)
441  {
442  EntrySpeed = TempEntrySpeed;
443  SetTrainMovementValues(3, NextElementPosition, NextEntryPos);
444  }
445  }
446  }
447  }
449  {
450  throw Exception("Error, LeadElement Exit Connection is NotSet");
451  }
452  }
453 
454  if(MidElement > -1) // will be -1 if start on continuation
455  {
456  Straddle = LeadMid;
460  {
461  for(int x = 0; x < 4; x++)
462  {
463  HeadCodePosition[x] = HeadCodeGrPtr[3 - x];
464  }
465  }
466  else
467  {
468  for(int x = 0; x < 4; x++)
469  {
471  }
472  }
473  if(TrainMode == Timetable)
475  else
478  // pick up background bitmaps [0] & [1] & plot HeadCodes [0] & [1]
479 
482 /* Move check to AddTrain, also, now that can start on bridges need to check that other train is on same track before refusing
483  if((Track->TrackElementAt(182, LeadElement).TrainIDOnElement > -1) || ((MidElement > -1) && (Track->TrackElementAt(183, MidElement).TrainIDOnElement > -1)))
484  {
485  ShowMessage("Can't place train " + HeadCode + "; another train already present at location");
486  Utilities->CallLogPop(651);
487  return false;
488  }
489 */
494  PlotTrainGraphic(8, 0, Display);
495  PlotTrainGraphic(9, 1, Display);
496 
499 
500  // pick up background bitmaps [2] & [3]
501 
504 
505  PlotElement[2] = MidElement;
507  PlotElement[3] = MidElement;
509  PlotTrainGraphic(10, 2, Display);
510  PlotTrainGraphic(11, 3, Display);
511  // Plotted = true; set in PlotTrainGraphic
512  }
513  Display->Update(); // resurrected when Update() dropped from PlotOutput etc
514  Utilities->CallLogPop(652);
515 }
516 
517 // ---------------------------------------------------------------------------
518 void TTrain::UnplotTrain(int Caller)
519 {
520  // Note: If trouble is experienced with the PlotAlternativeTrackRouteGraphic functions remove them & test for train on a bridge and if so call Clearand..
521  if(!Plotted)
522  return;
523 
524  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UnplotTrain," + HeadCode);
525 
526  if(Straddle == MidLag)
527  {
528  if(MidElement > -1)
529  {
534  // to force plot of locked route marker, needed once only for the element
535  }
536  if(LagElement > -1)
537  {
542  // to force plot of locked route marker, needed once only for the element
543  }
544  }
545  else if(Straddle == LeadMidLag)
546  {
547  if(LeadElement > -1)
548  {
551  // to force plot of locked route marker, needed once only for the element
552  }
553  if(MidElement > -1)
554  {
559  // to force plot of locked route marker, needed once only for the element
560  }
561  if(LagElement > -1)
562  {
565  // to force plot of locked route marker, needed once only for the element
566  }
567  }
568  else if(Straddle == LeadMid)
569  {
570  if(LeadElement > -1)
571  {
576  // to force plot of locked route marker, needed once only for the element
577  }
578  if(MidElement > -1)
579  {
584  // to force plot of locked route marker, needed once only for the element
585  }
586  }
587 
588  if(LeadElement > -1)
590  if(MidElement > -1)
592  if(LagElement > -1)
594  Plotted = false;
596  Display->Update();
597  // without this the screen 'blinks' at next Clearand... prob forces a full repaint for some reason
598  // resurrected when Update() dropped from PlotOutput etc
599  Utilities->CallLogPop(653);
600 }
601 
602 // ----------------------------------------------------------------------------
603 
604 void TTrain::UpdateTrain(int Caller)
605 /*
606  Note: Some changes made since comments written
607 
608  Brief:
609  Enter with Straddle defining train position wrt Lag, Mid & Lead elements. Is only MidLag at this point
610  on first entry at a continuation (with no train plotted), in all other cases it is either LeadMid (when train fully
611  on Lead & Mid elements) or LeadMidLag (when train straddling 3 elements).
612  Thereafter on entry Straddle = LeadMidLag or LeadMid; LeadMid if train fully on Mid & Lead elements, and
613  LeadMidLag if on Lag, Mid and Lead elements (back on lag, front on Lead, & middle 2 segments on Mid).
614  If enter with Straddle = LeadMid, then train is in effect in the first half of the next element, and moves half onto it after
615  the half time point has been passed. The values for the next element were set when the train was last updated when Straddle became
616  LeadMid from LeadMidLag. After the half time point has been passed Straddle is
617  changed to MidLag within the function and all elements moved down one, old Mid becomes
618  the new lag, old Lead becomes the new Mid, and a new Lead is obtained. Then the new positions are plotted, and finally Straddle is
619  incremented to reflect the position the train now occupies.
620 
621  Detail:
622  Set TrainFailurePending if all conditions met
623  Check whether stopped at a non-red signal, and if so reset StoppedAtSignal so train can move.
624  Check whether buffers at immediate exit, either when first enter the function or later, and set StoppedAtBuffers if so
625  and return.
626  If Straddle == LeadMid then train fully on Lead and Mid, so ready for a major update:-
627  If there's a LagElement (there will be but include check for good practice - next
628  function depends on it) Check whether DerailPending set - set during last GetLeadElement if appropriate but only acted on here when
629  train fully on offending point - Derail set and DerailPanding reset, train background
630  colour changed (note that BackgroundColour is a property of the train itself) then return.
631  If no derail pending reset Lag and Mid elements to the old Mid and Lead values, reset Straddle to MidLag, then set
632  the new LeadElement, which will be the next connected element (obtain using GetLeadElement) or -1 if the current
633  LeadElement is an exit continuation. During GetLeadElement the element at LeadElement is checked and if a stop
634  signal is found StoppedAtSignal is set to true, otherwise StoppedAtSignal is set to false. Also Derail is set
635  if LeadElement is a fouled trailing point.
636  Now, the train is moved on by one segment. Firstly the last BackgroundElement is set to LagElement, then the last
637  segment of the LagElement is unplotted (if there is a LagElement - may be entering at a continuation), by
638  replotting the last background segment and checking whether the element is a bridge or crossover with the other
639  track in a route, in which case the route colour is replotted.
640  Then, if Straddle == LeadMidLag (train will move completely off the element during this function), and the train
641  track is in a route, then all the train elements are removed from the route unless it's an autosig route. Normally only the
642  LeadElement will be in a route for a moving train, but when originally placed all elements may be in the route so check them all.
643  Note also that there may be two routes at a given element position, but only one of them is the correct one, so this
644  is identified prior to the removal. Also the TrainIDs are reset because the train will be fully off this element at the end of
645  the function. If Straddle == LeadMidlag and the element being left is a ContinuationExit the the TrainGone flag is set so the
646  train can be deleted by the calling function, and the function returns.
647  If the element is a signal in the train movement direction, then it is reset to red (Attribute = 0) and is replotted
648  to show the red aspect. Finally if element is a signal in the other direction it is replotted as it was - need to
649  plot individually because could have any aspect, the background bitmap that was picked up earlier contains just the
650  basic red aspect.
651 
652  Now all the array values are updated, but the [0] values are as yet invalid, these have to be obtained explicitly from
653  the new LeadElement later. The headcode graphics are updated so that it reads correctly - left to right & top to bottom,
654  regardless of direction, and with the correct front code colour.
655 
656  The new front segment background bitmap is now picked up and the graphic offsets set, and the segments are plotted.
657  No more unplotting is needed as all but the last segment are overwritten by later segments, and the new front
658  segment is just plotted, though the background bitmap at that location has to be picked up. Just where they are
659  plotted depends on the Straddle value, [0] is always on Lead, [1] is on Lead if Straddle == LeadMidLag or Mid if
660  Straddle == MidLag; [2] is always on Mid, and [3] is on Mid if Straddle == LeadMidLag or Lag if Straddle == MidLag.
661  Also prior to plotting the lead segment a crash check is made, and if true the Crashed flag is set and the
662  TrainCrashedInto value also set to the current TrainID - this is so it too becomes crashed and hence stopped.
663 
664  The Crashed flag is now checked, and if set the front headcode colour is changed to the same as the rest of the code,
665  and the background colour changed. Then the train that is crashed into is also set to Crashed, and its colours
666  changed similarly. The function then returns.
667 
668  If Crashed is not set then Straddle is incremented and the function returns.
669 */
670 
671 {
672  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UpdateTrain," + HeadCode);
673  UpdateCounter++;
674  // 100 counts = 5secs (used in splits to prevent too frequent length checks in front & rear splits)
675  if(UpdateCounter >= 100)
676  UpdateCounter = 0;
677 
678  int RandRange = (TrainController->MTBFHours * 3600) / 53;
679 
680  // MTBFHours is in timetable clock hours, min value is 1 & max value is 10,000 (integer values on input)
681  // but double on use because it represents timetable clock time, so at 1/16 speed RandRange is * 16 (160,000 max) & at 16x speed its /16 (1/16 min)
682  // i.e MTBFHours is Input value/TTClockSpeed (conversion is done in InterfaceUnit)
683  if(int(TrainController->RandomFailureCounter) == (rand() % 1060))
684  // RandomFailureCounter value is fixed for a full cycle of train updates so this
685  // makes sure there's no bunching of failures as there is for a fixed comparison number
686  // or a small range of comparison numbers. True every 53 secs (real time) on average rand()
687  // gives a random number between 0 and 32767 (defined as RAND_MAX in stdlib.h)
688  {
689  if(!TrainFailed && !TrainOnContinuation(0) && (RandRange > 0) && (PowerAtRail > 1) && !((TrainMode == Timetable) && TimetableFinished)
690  && !Crashed && !Derailed && !((TrainMode == Signaller) && Stopped()))
691  // RandomFailureCounter resets to 0 every 53 secs, if RandRange is 0 then no failure rate is set - i.e. failure rate = 0
692  // don't fail if:
693  // (a) on a continuation (entering or leaving);
694  // (b) already failed;
695  // (c) power effectively zero (8000) min value for powered; 0.08 for 'no power';
696  // (d) train terminated;
697  // (e) crashed or derailed; or
698  // (f) under signaller control and stopped.
699  {
700  if((random(RandRange)) == 0)
701  // max value for RandRange is over 2x10^9
702  { // here if failure due
703  TrainFailurePending = true;
704  // fail when PlotElements set to proper Lead, Mid & Elements
705  }
706  }
707  }
708 
709 /* dropped as it allows a train to stop on a half element - when reach (if Stopped()) at line 1310
710  if ((PowerAtRail < 1) && (EntrySpeed < 1)) // added at v2.4.0
711  {
712  StoppedWithoutPower = true;
713  }
714 */
715  int LockedVectorNumber;
716  Graphics::TBitmap *EXGraphicPtr = RailGraphics->bmTransparentBgnd;
717  // default values - these needed for route checker below
718  Graphics::TBitmap *EntryDirectionGraphicPtr = RailGraphics->bmTransparentBgnd;
719 
722 
723  if(Crashed || Derailed)
724  {
726  {
727  PlotTrain(7, Display);
728  // replotted every cycle because of level crossing crashes, otherwise a flashing level crossing wipes out half of the train
729  Display->Update();
730  }
731  OpTimeToAct = 0.0;
732  // need to set this here as wouldn't be calculated otherwise as return from UpdateTrain
733  Utilities->CallLogPop(1017);
734  return; // no further action, user has to remove or work around
735  }
736 
738  {
740  }
741 
743  {
745  }
746 
748  // introduced at v1.2.0, formerly 'TimeTimeLocArrived = false' was included
749  // in the next condition 'if(!Stopped() && !SPADFlag)' which led to repeated arrival messages if signaller control allowed a train
750  // to move & then stop again at the same station
751  {
752  TimeTimeLocArrived = false;
753  }
754 
755  if(!Stopped() && !SPADFlag && !TrainFailed)
756  {
758  }
759 
760  // set or release StoppedAtBuffers if fully on 2 elements depending on LeadElement
761  // Note that if LeadElement == Buffers train must be facing the buffer so no need to check orientation
762 /* old version where force a stop at buffers regardless of speed
763  if((Straddle == LeadMid) && (LeadElement > -1) && (Track->TrackElementAt(, LeadElement).TrackType == Buffers)) StoppedAtBuffers = true;
764  else StoppedAtBuffers = false;
765 */
766 
767  // new version where crash if run into buffers
768  if(!Crashed)
769  {
770  if((Straddle == LeadMid) && (LeadElement > -1) && (Track->TrackElementAt(602, LeadElement).TrackType == Buffers))
771  {
772  if(ExitSpeedFull > 1)
773  {
774  Crashed = true;
778  // SendMissedActionLogs(3, -1, ActionVectorEntryPtr);//-1 is a marker for send messages for all remaining entries, including Fer if present
779  // no need for missed action logs - will be sent when train removed
780  StoppedAtBuffers = false;
781  }
783  // stopped at location & stopped without power take precedence
784  {
785  StoppedAtBuffers = true;
786  }
787  else
788  StoppedAtBuffers = false;
789  }
790  else
791  StoppedAtBuffers = false;
792  }
793  else
794  StoppedAtBuffers = false;
795  // if crashed don't want stopped at buffers set
796 
797  // also crash if run into a level crossing that is changing or has barriers up
798  if(!Crashed)
799  {
800  if((Straddle == LeadMid) && (LeadElement > -1) && (ExitSpeedFull > 1))
801  {
802  int H = Track->TrackElementAt(873, LeadElement).HLoc;
803  int V = Track->TrackElementAt(874, LeadElement).VLoc;
804  if(Track->IsLCAtHV(40, H, V) && !Track->IsLCBarrierDownAtHV(2, H, V))
805  {
806  Crashed = true;
810  // no need for missed action logs - will be sent when train removed
811  }
812  }
813  }
814 
816  {
818  }
819 
820  // set or reset HoldAtLocationInTTMode (if true then actions are needed before train departs)
821  if((TrainMode == Timetable) && StoppedAtLocation && (ActionVectorEntryPtr->Command != "")) //if Command == "" then either TimeLoc or TimeTimeLoc
822  HoldAtLocationInTTMode = true;
823  else if(TrainMode == Timetable)
824  HoldAtLocationInTTMode = false;
825  // in Signaller mode HoldAtLocationInTTMode not changed
826 
827  // check if departure pending & set times unless already set
828  if(TrainMode == Timetable)
829  {
831  // && !StoppedAtBuffers) - drop this, set times whether or not at buffers
832  {
833  if(ActionVectorEntryPtr->DepartureTime > TDateTime(-1))
834  {
836  if(ReleaseTime <= LastActionTime + TDateTime(30.0 / 86400))
837  ReleaseTime = LastActionTime + TDateTime(30.0 / 86400);
838  TRSTime = ReleaseTime - TDateTime(10.0 / 86400);
839  DepartureTimeSet = true;
840  }
841  }
842  }
843 
845  {
846  OpTimeToAct = CalcTimeToAct(0); // called after ReleaseTime set
847  // calculate every 1 sec (in real time, not timetable time) for all trains
848  }
849 
850  // check if being held at location pending any actions & deal with them if time appropriate & >= 30s since LastActionTime
851  if(TrainMode == Timetable)
852  {
853  if((ActionVectorEntryPtr->Command != "Frh") && (ActionVectorEntryPtr->Command != "Frh-sh"))
854  {
855  RemainHereLogNotSent = true;
856  }
858  {
859  // ignore TimeLoc & TTLoc departures
860  // Action logs given in functions
862  LastActionTime + TDateTime(30.0 / 86400)))
863  {
864  if(ActionVectorEntryPtr->Command == "fsp")
865  { // added for v1.3.2 because when add new train to TrainVector 'this' address likely invalidated, hence make no more changes to 'this' train. Next clock cycle will deal with any required changes
866  FrontTrainSplit(0);
867  if(TrainFailurePending) // ok, stopped so PlotElements set
868  {
869  TrainHasFailed(0);
870  }
871  Utilities->CallLogPop(2041);
872  return;
873  }
874  else if(ActionVectorEntryPtr->Command == "rsp")
875  { // added for v1.3.2 because when add new train to TrainVector 'this' address likely invalidated, hence make no more changes to 'this' train. Next clock cycle will deal with any required changes
876  RearTrainSplit(0);
877  if(TrainFailurePending) // ok, stopped so PlotElements set
878  {
879  TrainHasFailed(1);
880  }
881  Utilities->CallLogPop(2042);
882  return;
883  }
884  else if(ActionVectorEntryPtr->Command == "Fjo")
885  FinishJoin(0);
886  else if(ActionVectorEntryPtr->Command == "jbo")
887  JoinedBy(0);
888  else if(ActionVectorEntryPtr->Command == "cdt")
890  else if(ActionVectorEntryPtr->Command == "Fns")
891  NewTrainService(0);
892  else if(ActionVectorEntryPtr->Command == "Frh")
893  RemainHere(0);
894  else if(ActionVectorEntryPtr->Command == "Fer")
895  TimetableFinished = true;
896  // other aspects of 'Fer' dealt with in TTrain::Update()
897  else if(ActionVectorEntryPtr->Command == "F-nshs")
899  else if(ActionVectorEntryPtr->Command == "Frh-sh")
901  else if(ActionVectorEntryPtr->Command == "Fns-sh")
903 /*
904  F-nshs (FNSShuttle) = Finish New Service (Shuttle) = finish, form new shuttle service in same direction, details =
905  shuttle headcode (no train creation)
906  Frh-sh (TimeCmdHeadCode) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done
907  remain here
908  Fns-sh (FSHNewService) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done
909  form new service via Sns-fsh using the NonRepeatingShuttleLinkHeadCode
910 */
911  }
912  }
913  else
914  {
916  {
918  }
919  }
920  }
921  if(TrainMode == Timetable)
922  {
923  if(StoppedAtBuffers)
924  {
925  // error if buffers (& element before it) not at a location, or if buffer location different to ActionVectorEntryPtr location
926  // if buffer location same as ActionVectorEntryPtr location & not Frh then error will be given for inability to depart
927  AnsiString BufferLocation = Track->TrackElementAt(604, LeadElement).ActiveTrackElementName;
928  if(BufferLocation == "")
930  AnsiString ExpectedLocation = ActionVectorEntryPtr->LocationName;
931  if((BufferLocation == "") || (BufferLocation != ExpectedLocation))
932  {
936  {
938  // SendMissedActionLogs(-1, ActionVectorEntryPtr);//-1 is a marker for send messages for all remaining entries, including Fer if present
939  // Drop missed actions so user can still use sig mode to get back on track
941  }
942  if(TrainFailurePending) // ok, stopped so PlotElements set
943  {
945  TrainHasFailed(2);
946  }
947  Utilities->CallLogPop(1020);
948  return;
949  }
950  else if((BufferLocation != "") && (BufferLocation == ExpectedLocation) && DepartureTimeSet && !StoppedAtLocation && (TrainController->TTClockTime >
951  ReleaseTime))
952  {
955  {
958  // SendMissedActionLogs(-1, ActionVectorEntryPtr);//-1 is a marker for send messages for all remaining entries, including Fer if present
959  // Drop missed actions so user can still use sig mode to get back on track
961  }
962  if(TrainFailurePending) // ok, stopped so PlotElements set
963  {
965  TrainHasFailed(3);
966  }
967  Utilities->CallLogPop(1397);
968  return;
969  }
970  }
971  else
972  {
974  }
975  }
976  else
977  {
979  }
980 
981  if(TrainMode == Timetable)
982  {
984  {
986  }
988  {
990  }
991  }
992 
993  // Pick up element next to the train front (if exists) to check for calling-on, restart after a cleared signal, or
994  // restart after stopped for train in front
995  int NextElementPosition, NextEntryPos;
996 
997  if(LeadElement > -1) // if an exit continuation then not set
998  {
999  if((Track->TrackElementAt(186, LeadElement).TrackType != Points) || ((LeadEntryPos != 0) && (LeadEntryPos != 2)))
1000  {
1002  }
1003  else if((Track->TrackElementAt(187, LeadElement).TrackType == Points) && ((LeadEntryPos == 0) || (LeadEntryPos == 2)))
1004  {
1005  if(Track->TrackElementAt(188, LeadElement).Attribute == 0)
1006  {
1007  LeadExitPos = 1;
1008  }
1009  else
1010  {
1011  LeadExitPos = 3;
1012  }
1013  }
1014  NextElementPosition = Track->TrackElementAt(189, LeadElement).Conn[LeadExitPos];
1015  NextEntryPos = Track->TrackElementAt(190, LeadElement).ConnLinkPos[LeadExitPos];
1016  }
1017  else
1018  {
1019  NextElementPosition = -1;
1020  NextEntryPos = -1;
1021  }
1022 
1023  if((NextElementPosition > -1) && (NextEntryPos > -1))
1024  // may be buffers or continuation so need this check
1025  {
1026 /*
1027  Check whether calling-on conditions met:-
1028  a) approaching train has stopped at a signal but not at a location;
1029  b) if there is a facing train at the station, it is being held appropriately (must be awaiting a join (Fjo or jbo) or a
1030  change of direction (cdt), remaining here (Frh), or under signaller control);
1031  c) at least 1 platform available for the approaching train;
1032  d) points (if any) set for direct route into platform;
1033  e) approaching train is to stop at station;
1034  f) no more facing signals between train and platform;
1035  g) [dropped g]
1036  h) train in front preventing route being set far enough to release stop signal;
1037  i) train in front not exiting at continuation;
1038  j) signal must be within 4km of the stop platform;
1039  k) [dropped (k), now can set a reoute or part route into platform so can set points more easily]; and
1040  l) no existing route conflicts with the route into the platform.
1041  If all OK & route or part route not already set then set an unrestricted route into the station (just to the first platform), to prevent point changing or
1042  other route conflicts - if a partial route set than can still change points outside the route or have a route conflict if another route is set.
1043 */
1044  if(TrainMode == Timetable)
1045  {
1046  if(CallingOnAllowed(0))
1047  {
1048  CallingOnFlag = true;
1049  PlotTrainWithNewBackgroundColour(1, clCallOnBackground, Display); // calling-on background
1050  }
1051  else
1052  {
1053  if(CallingOnFlag)
1054  {
1056  }
1057  CallingOnFlag = false;
1058  }
1059  }
1060  if(StoppedAtSignal && ((Track->TrackElementAt(191, NextElementPosition).Attribute > 0) || AllowedToPassRedSignal) && !TrainFailed)
1061  {
1062  // reset PassRedSignal when reached half-way point in next element, if reset here then SetTrainMovementValues
1063  // sets StoppedAtSignal again & train doesn't move
1064  StoppedAtSignal = false;
1065  // need to recalculate exit times since old entry time expired. Straddle now at MidLag with front of train on MidElement
1066  // hence use MidElement for the calculation so same as would have been used if signal not red, when Straddle was
1067  // LeadMidLag and front of train was on LeadElement (after the current move)
1069  EntrySpeed = 0;
1071  FirstHalfMove = true;
1072  SetTrainMovementValues(4, NextElementPosition, NextEntryPos);
1073  // NextElement is the element to be entered
1074  }
1076  {
1077  if(ClearToNextSignal(0))
1078  {
1079  StoppedForTrainInFront = false;
1080  BeingCalledOn = false;
1081  EntrySpeed = 0;
1083  FirstHalfMove = true;
1084  SetTrainMovementValues(16, NextElementPosition, NextEntryPos);
1085  }
1086  else
1087  {
1088  if(TrainFailurePending) // ok, stopped so PlotElements set
1089  {
1090  TrainHasFailed(4);
1091  }
1092  Utilities->CallLogPop(1097);
1093  return;
1094  }
1095  }
1096  }
1097 
1098  if((Straddle == MidLag) && (LeadElement != -1))
1099  // later check only for Straddle == LeadMid, so need this check here for initial train start
1100  {
1102  }
1103 
1104 /* Logic below as follows: This check is made to allow a restart if had StoppedAtLocation or StoppedForTrainInFront or
1105  both but potentially able to restart (i.e. not at buffers, not crashed, not derailed, not held at location, departure
1106  time due, no train in front now & no other stop condition). Note that can be StoppedForTrainInFront when not at a
1107  location since this is set in SetTrainMovementValues whenever a train has zero EntrySpeed and there is a train in front,
1108  which could be when start as Snt.
1109  If StoppedForTrainInFront but not StoppedAtLocation then need to set TRSTime high so pink not plotted, and ReleaseTime
1110  low so can restart if appropriate. BeingCalledOn was set so that when train stopped at a station it wouldn't restart
1111  until the line was clear of trains up to the next signal. Hence check whether BeingCalledOn & if so set
1112  StoppedForTrainInFront, this ensures two things - that the restart check is carried out at each cycle and also that
1113  a restart won't happen until the line is clear to the next signal, regardless of whether or not the ReleaseTime has been
1114  reached.
1115  Then check if TRS time reached & change background to pink if so, & check if release time reached & if so change
1116  background to white and clear StoppedAtLocation. Then make check of station name, and recheck StoppedForTrainInFront,
1117  if it's set check if ClearToNextSignal and if so clear StoppedForTrainInFront & BeingCalledOn. If not ClearToNextSignal
1118  then return. If either not StoppedForTrainInFront or ClearToNextSignal then restart, calling SetTrainMovementValues &
1119  sending a message to the performancelog.
1120 */
1121 
1122  if(TrainMode == Timetable)
1123  {
1125  {
1126  if(BeingCalledOn)
1127  {
1128  StoppedForTrainInFront = true;
1129  }
1131  {
1133  }
1135  {
1136  // value updated at every scheduled departure & arrival
1138  AnsiString StationName;
1140  {
1142  }
1144  {
1146  }
1147  else
1148  {
1149  throw Exception("Error - Stopped at through station but neither lead nor mid elements have a name");
1150  }
1151  EntrySpeed = 0;
1155  FirstHalfMove = true;
1156  StoppedAtLocation = false;
1157 
1158  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
1159  {
1160  StoppedWithoutPower = true;
1161  }
1162 
1163  if((NextElementPosition > -1) && (NextEntryPos > -1))
1164  // condition check added for SloughIECC error reported by James U
1165  {
1166  if((Track->TrackElementAt(720, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
1167  (Track->TrackElementAt(721, NextElementPosition).Attribute == 0))
1168  {
1169  StoppedAtSignal = true;
1170  if(!StoppedWithoutPower)
1171  // if stopped without power just keep existing background colour
1172  {
1174  // TrainController->LogActionError(40, HeadCode, "", SignalHold, Track->TrackElementAt(755, NextElementPosition).ElementID);
1175  }
1176  }
1177  }
1179  {
1180  TimeTimeLocArrived = false;
1181  LogAction(27, HeadCode, "", Depart, StationName, ActionVectorEntryPtr->DepartureTime, false);
1182  // no warning for TimeTimeLoc departure
1183  }
1184  else
1185  {
1187  }
1188  DepartureTimeSet = false;
1189  // no need to set LastActionTime for a departure
1191  // advance pointer beyond departure action - (this line (& LogAction) used to be at the end - see
1192  // note
1193 /*
1194  Note: If train stops at station after call on with a TimeTimeLoc loaded, and before the normal stop point, then when
1195  SetTrainMovementValues called it assumes a stop at the stop point because the ActionVectorEntryPtr points to a name
1196  when NameInTimetableBeforeCDT is called and the stop positions are valid. So next element train movement is based on
1197  this calculation. However, when the departure time check is made (it is during this function when SetTrainMovementValues
1198  is called), the ActionVectorEntryPtr is advanced at the end past the departure location, so at the next element when
1199  SetTrainMovementValues is called again, all is normal, i.e. the train doesn't stop again at the location. But to cure
1200  the problem move the ActionVectorEntryPtr increment to before SetTrainMovementValues.
1201 */
1203  {
1204  StoppedAtBuffers = true;
1205  }
1206  else if(!StoppedWithoutPower)
1207  // if buffers or no power, don't set values
1208  {
1210  {
1211  SetTrainMovementValues(12, NextElementPosition, NextEntryPos);
1212  // NextElement is the element to be entered
1213  }
1214  else
1215  {
1217  // use LeadElement for an exit continuation
1218  }
1219  }
1220  }
1221  }
1222  }
1223 
1224  if(Straddle == LeadMidLag)
1225  {
1227  {
1228  Utilities->CallLogPop(654);
1229  return;
1230  }
1231  }
1232  else
1233  {
1235  {
1236  Utilities->CallLogPop(655);
1237  return;
1238  }
1239  }
1240 
1241  if((LeadElement > -1) && (MidElement > -1))
1242  {
1244  { // don't allow to stop if exiting at a continuation as causes problems if try to change direction
1245  // if entering at continuation & LeadElement is a continuation then MidElement will be -1
1246  //don't need to check for MidElement being continuation because popup menu won't show when exiting at continuation so SignallerStoppingFlag can't be set
1247  SignallerStoppingFlag = false;
1248  StepForwardFlag = false;
1249  }
1250  }
1251 
1252  if(Stopped())
1253  // this is what prevents another movement if the train is stopped
1254  {
1255  if(TrainFailurePending) // ok, stopped so PlotElements set
1256  {
1257  TrainHasFailed(5);
1258  }
1259  BrakeRate = 0;
1260  Utilities->CallLogPop(656);
1261  return;
1262  }
1263 
1264  // here when ready for next move
1265 
1266  // check for train in front & if so stop at next access (when train fully on element next to train)
1267  if((TrainMode == Signaller) && (Straddle == LeadMidLag))
1268  // SetTrainMovementValues brakes & stops signaller mode train for a train in front using local
1269  // variable TrainInFrontInSignallerModeFlag
1270  {
1271  if(LeadElement > -1)
1272  {
1273  int NextPos = Track->TrackElementAt(649, LeadElement).Conn[LeadExitPos];
1274  int NextEntryPos = Track->TrackElementAt(650, LeadElement).ConnLinkPos[LeadExitPos];
1275  if(Track->OtherTrainOnTrack(1, NextPos, NextEntryPos, TrainID))
1276  // true if another train on NextEntryPos track whether bridge or not
1277  {
1278  StoppedForTrainInFront = true;
1279  }
1280  else
1281  {
1282  StoppedForTrainInFront = false;
1283  }
1284  }
1285  }
1286 
1287  if((Straddle == LeadMid) && SPADFlag)
1288  // give message + plot background when ready to move half past the signal
1289  {
1290  if(NextElementPosition > -1)
1291  {
1292  if((Track->TrackElementAt(662, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
1293  (Track->TrackElementAt(663, NextElementPosition).Attribute == 0))
1294  {
1295  AnsiString LocID = AnsiString(Track->TrackElementAt(664, NextElementPosition).ElementID);
1297  // if goes past 2 signals then give message twice
1299  }
1300  }
1301  }
1302 
1303  if(Straddle == LeadMidLag)
1304  // During this function train moves fully onto 2 elements, Lead & Mid, so set next 2 moves from here for the element after Lead
1305  {
1306  // if SPADFlag set allow to keep moving until signal obscured before setting background colour, & stop only when ExitSpeedFull is 0
1307  if(SPADFlag)
1308  {
1309  if(ExitSpeedFull == 0)
1310  {
1311  StoppedAfterSPAD = true;
1312  // but don't want to stop until have moved fully onto element, hence stop test is before this check
1313  }
1314  }
1315 
1317  {
1318  if(ExitSpeedFull == 0)
1319  {
1320  // only reach here when will stop on LeadMid, because SetTrainMovementValues called after this (i.e. ExitSpeedFull becomes 0 if not 0 now
1321  // after this test), and Straddle == LeadMidLag so not accessed at the half-move point, hence only reached at the full move
1322  // point when the speed is 0. So, colour change won't occur until fully stopped (early in UpdateTrain()), and the log message
1323  // is sent at the right time and once only.
1324  SignallerStopped = true;
1325  // but don't want to stop until have moved fully onto element, hence stop test is before this check
1326  StepForwardFlag = false;
1327  SignallerStoppingFlag = false;
1328  TTrackElement TE;
1329  AnsiString Loc = "";
1330  bool LocNamed = false;
1331  if(LeadElement > -1)
1332  {
1333  TE = Track->TrackElementAt(782, LeadElement);
1334  if(TE.ActiveTrackElementName != "")
1335  {
1336  Loc = TE.ActiveTrackElementName;
1337  LocNamed = true;
1338  }
1339  else
1340  {
1341  Loc = "track element " + TE.ElementID;
1342  }
1343  }
1344  if((MidElement > -1) && !LocNamed)
1345  {
1346  TE = Track->TrackElementAt(783, MidElement);
1347  if(TE.ActiveTrackElementName != "")
1348  {
1349  Loc = TE.ActiveTrackElementName;
1350  LocNamed = true;
1351  }
1352  else if(Loc == "")
1353  {
1354  Loc = "track element " + TE.ElementID;
1355  }
1356  }
1357  if(Loc == "")
1358  {
1359  Loc = "outside railway";
1360  // must have stopped after left at a continuation (because both lead & mid == -1)
1361  }
1362  else
1363  Loc = "at " + Loc;
1364  LogAction(30, HeadCode, "", SignallerStop, Loc, TrainController->TTClockTime, false); // false for warning
1365  }
1366  }
1367  if(LeadElement > -1) // if an exit continuation then not set
1368  {
1369  if((Track->TrackElementAt(202, LeadElement).TrackType != Points) || ((LeadEntryPos != 0) && (LeadEntryPos != 2)))
1370  {
1372  }
1373  else if((Track->TrackElementAt(203, LeadElement).TrackType == Points) && ((LeadEntryPos == 0) || (LeadEntryPos == 2)))
1374  {
1375  if(Track->TrackElementAt(204, LeadElement).Attribute == 0)
1376  LeadExitPos = 1;
1377  else
1378  LeadExitPos = 3;
1379  }
1380  NextElementPosition = Track->TrackElementAt(205, LeadElement).Conn[LeadExitPos];
1381  NextEntryPos = Track->TrackElementAt(206, LeadElement).ConnLinkPos[LeadExitPos];
1382  }
1383  else
1384  {
1385  NextElementPosition = -1;
1386  NextEntryPos = -1;
1387  }
1388 
1391  FirstHalfMove = true; //will be when finished the move onto 2 elements during this function
1392 
1393  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
1394  {
1395  StoppedWithoutPower = true;
1396  }
1397 
1398  if((NextElementPosition > -1) && (NextEntryPos > -1) && !SPADFlag)
1399  // may be buffers or continuation. SPADFlag added at v2.1.0
1400  // so don't override the SPAD colour & don't set StoppedAtSignal
1401  {
1402  if((Track->TrackElementAt(207, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
1403  (Track->TrackElementAt(208, NextElementPosition).Attribute == 0) && (ExitSpeedFull < 1) && !StoppedAtLocation)
1404  {
1405  StoppedAtSignal = true;
1406  if(!StoppedWithoutPower)
1407  // leave background as is if no power, but set StoppedAtSignal
1408  {
1410  }
1411  // TrainController->LogActionError(41, HeadCode, "", SignalHold, Track->TrackElementAt(756, NextElementPosition).ElementID);
1412  }
1413  }
1414 
1415  if(!Stopped())
1416  {
1417  if((NextElementPosition > -1) && (NextEntryPos > -1))
1418  // may be buffers or continuation (skip SetTrainMovementValues if buffers, if
1419  // a stop element that isn't buffers - e.g. station, then will skip the calcs
1420  // during SetTrainMovementValues to avoid trying to divide by zero - see that
1421  // function for fuller explanation
1422  {
1423  SetTrainMovementValues(8, NextElementPosition, NextEntryPos);
1424  // NextElement is the element to be entered
1425  }
1426  // follow the continuation exits:-
1427  else if((LeadElement > -1) && (Track->TrackElementAt(209, LeadElement).TrackType == Continuation))
1428  {
1430  // Use LeadElement for calcs if lead is a continuation
1431  }
1432  else if((MidElement > -1) && (Track->TrackElementAt(210, MidElement).TrackType == Continuation))
1433  {
1435  // Use MidElement for calcs if mid is a continuation
1436  }
1437  else if((LagElement > -1) && (Track->TrackElementAt(211, LagElement).TrackType == Continuation))
1438  {
1440  // Use LagElement for calcs if lag is a continuation
1441  }
1442  }
1443 
1444  // remove route elements if not autosigs - this section moved from below, was under LagElement > -1 condition but needs to cover LagElement == -1
1445  if(AllRoutes->GetRouteTypeAndGraphics(2, LeadElement, LeadEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::NotAutoSigsRoute)
1446  // Trains may not be in a route
1447  // Since Straddle = LeadMidLag at this point the train is going to move fully off the existing Lag & fully onto existing Lead element during this function
1448  {
1449  // NB if LeadElement == -1 then the above test returns false
1450  int TempH = Track->TrackElementAt(213, LeadElement).HLoc;
1451  int TempV = Track->TrackElementAt(214, LeadElement).VLoc;
1452  int TempELink = Track->TrackElementAt(215, LeadElement).Link[LeadEntryPos];
1453  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
1454  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(10, TempH, TempV, SecondPair);
1455  if((FirstPair.first > -1) && (AllRoutes->GetFixedRouteAt(143, FirstPair.first).GetFixedPrefDirElementAt(153,
1456  FirstPair.second).GetELink() == TempELink))
1457  {
1458  AllRoutes->RemoveRouteElement(10, TempH, TempV, TempELink);
1459  }
1460  else if((SecondPair.first > -1) && (AllRoutes->GetFixedRouteAt(144, SecondPair.first).GetFixedPrefDirElementAt(154,
1461  SecondPair.second).GetELink() == TempELink))
1462  {
1463  AllRoutes->RemoveRouteElement(11, TempH, TempV, TempELink);
1464  }
1465  }
1466 
1467  if(AllRoutes->GetRouteTypeAndGraphics(3, MidElement, MidEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::NotAutoSigsRoute)
1468  // Trains may not be in a route
1469  {
1470  int TempH = Track->TrackElementAt(216, MidElement).HLoc;
1471  int TempV = Track->TrackElementAt(217, MidElement).VLoc;
1472  int TempELink = Track->TrackElementAt(218, MidElement).Link[MidEntryPos];
1473  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
1474  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(11, TempH, TempV, SecondPair);
1475  if((FirstPair.first > -1) && (AllRoutes->GetFixedRouteAt(145, FirstPair.first).GetFixedPrefDirElementAt(155,
1476  FirstPair.second).GetELink() == TempELink))
1477  {
1478  AllRoutes->RemoveRouteElement(12, TempH, TempV, TempELink);
1479  }
1480  else if((SecondPair.first > -1) && (AllRoutes->GetFixedRouteAt(146, SecondPair.first).GetFixedPrefDirElementAt(156,
1481  SecondPair.second).GetELink() == TempELink))
1482  {
1483  AllRoutes->RemoveRouteElement(13, TempH, TempV, TempELink);
1484  }
1485  }
1486 
1487  if(AllRoutes->GetRouteTypeAndGraphics(4, LagElement, LagEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::NotAutoSigsRoute)
1488  // Trains may not be in a route
1489  {
1490  int TempH = Track->TrackElementAt(219, LagElement).HLoc;
1491  int TempV = Track->TrackElementAt(220, LagElement).VLoc;
1492  int TempELink = Track->TrackElementAt(221, LagElement).Link[LagEntryPos];
1493  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
1494  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(12, TempH, TempV, SecondPair);
1495  if((FirstPair.first > -1) && (AllRoutes->GetFixedRouteAt(147, FirstPair.first).GetFixedPrefDirElementAt(157,
1496  FirstPair.second).GetELink() == TempELink))
1497  {
1498  AllRoutes->RemoveRouteElement(14, TempH, TempV, TempELink);
1499  }
1500  else if((SecondPair.first > -1) && (AllRoutes->GetFixedRouteAt(148, SecondPair.first).GetFixedPrefDirElementAt(158,
1501  SecondPair.second).GetELink() == TempELink))
1502  {
1503  AllRoutes->RemoveRouteElement(15, TempH, TempV, TempELink);
1504  }
1505 
1506  AllRoutes->CheckMapAndRoutes(8); // test
1507  }
1508 
1509  if(LagElement > -1)
1510  // not entering at a continuation so can deal with train leaving the lag element
1511  {
1513  // amended below so route elements removed for the complete train (for NotAutoSigsRoutes), so train never standing on a route once it
1514  // starts moving, covers for eliminating route when train reaches buffers, and prevents odd route segments when route extended while
1515  // straddling 3 elements (formerly the last segment was replotted as a route & stayed plotted
1516 
1517  TPrefDirElement PrefDirElement;
1518  // plot locked route marker for any element if appropriate (i.e. if a locked AutoSigs route) but only when train leaves element completely
1519  // as this is a 16x16 graphic
1521  {
1523  RailGraphics->LockedRouteCancelPtr[PrefDirElement.GetELink()]);
1524  }
1525 
1526  if(ContinuationExit(2, LagElement, LagExitPos)) // true if Element is a continuation and Exitpos is the continuation end
1527  {
1528  int RouteNumber;
1529  TrainGone = true;
1530  // flag to indicate train to be deleted - outside this function
1532  {
1533  TTrainController::TContinuationAutoSigEntry ContinuationAutoSigEntry;
1534  ContinuationAutoSigEntry.RouteNumber = RouteNumber;
1535  // calc distance from & inc last signal to exit
1536  int LastElement = LagElement, LastExitPos = LagExitPos, CumDistance = 0;
1537  int NewLastElement = 0, NewLastExitPos = 0;
1538  // need above because can't change LastElement & LastExitPos until both new values obtained
1539  // while((Track->TrackElementAt(684, LastElement).Config[LastExitPos] != Signal) && (CumDistance < 1200)) as was
1540  while((Track->TrackElementAt(913, LastElement).Config[LastExitPos] != Signal) && (CumDistance < 1200) && (Track->TrackElementAt(897,
1541  LastElement).TrackType != Points))
1542  // extra condition above added because of Moric1998's error (see email of 24/03/2016), where had an autosigs route across points, and another continuation on track not occupied by route so
1543  // failed when found a new element = -1 when tried to cross the continuation. Note this routine can only deal with non points as it uses GetNonPointsOppositeLinkPos
1544  // leave CumDistance as it was in these circumstances.
1545  {
1546  if(LastExitPos < 2)
1547  CumDistance += Track->TrackElementAt(685, LastElement).Length01;
1548  else
1549  CumDistance += Track->TrackElementAt(686, LastElement).Length23;
1550  NewLastElement = Track->TrackElementAt(687, LastElement).Conn[Track->GetNonPointsOppositeLinkPos(LastExitPos)];
1551  if(NewLastElement == -1)
1552  // this will catch buffers or any other connection failure
1553  {
1554  throw Exception("Error, Connection = -1 in Continuation loop in UpdateTrain");
1555  }
1556  NewLastExitPos = Track->TrackElementAt(688, LastElement).ConnLinkPos[Track->GetNonPointsOppositeLinkPos(LastExitPos)];
1557  if(NewLastExitPos == -1)
1558  {
1559  throw Exception("Error, ConnLinkPos = -1 in Continuation loop in UpdateTrain");
1560  }
1561  LastElement = NewLastElement;
1562  LastExitPos = NewLastExitPos;
1563  }
1564  // if at signal add this in too
1565  if(CumDistance < 1200)
1566  {
1567  CumDistance += Track->TrackElementAt(689, LastElement).Length01; // only need 01 for signal
1568  }
1569  // now have distance including the signal, if >=1200m use 100m (for a signal immediately after the continuation)
1570  // else use 1200m - CumDistance
1571  int FirstDistance = 0;
1572  if(CumDistance >= 1200)
1573  FirstDistance = 100;
1574  else
1575  FirstDistance = 1200 - CumDistance;
1576  if(FirstDistance < 100)
1577  FirstDistance = 100; // don't allow < 100
1578  // can now calc the time delays in seconds - FirstDelay, SecondDelay & ThirdDelay, these are doubles
1579  // BUT - first check whether ExitSpeedFull is very low (Mark had divide by zero error with zero exit speed using v2.4.0)
1580  if(ExitSpeedFull > 20.0)
1581  {
1582  ContinuationAutoSigEntry.FirstDelay = 3.6 * double(FirstDistance) / ExitSpeedFull;
1583  // speed in km/h & distance in m so mult by 3.6 to bring to secs
1584  ContinuationAutoSigEntry.SecondDelay = ContinuationAutoSigEntry.FirstDelay + 4320.0 / ExitSpeedFull;
1585  // 4320.0 = 3.6 * 1200, .0 to make it a double
1586  ContinuationAutoSigEntry.ThirdDelay = ContinuationAutoSigEntry.SecondDelay + 4320.0 / ExitSpeedFull;
1587  }
1588  else
1589  {
1590  ContinuationAutoSigEntry.FirstDelay = 60.0; // 60 secs
1591  ContinuationAutoSigEntry.SecondDelay = 120.0;
1592  ContinuationAutoSigEntry.ThirdDelay = 180.0;
1593  }
1594  ContinuationAutoSigEntry.AccessNumber = 0;
1595  ContinuationAutoSigEntry.PassoutTime = TrainController->TTClockTime;
1597  {
1599  for(VectorIT = TrainController->ContinuationAutoSigVector.begin(); VectorIT != TrainController->ContinuationAutoSigVector.end();
1600  VectorIT++)
1601  {
1602  if(VectorIT->RouteNumber == RouteNumber)
1603  {
1604  // another train has passed out of same route so erase earlier entry
1605  TrainController->ContinuationAutoSigVector.erase(VectorIT);
1606  break;
1607  }
1608  }
1609  }
1610  TrainController->ContinuationAutoSigVector.push_back(ContinuationAutoSigEntry);
1611  }
1613  // need to plot this as returning early so will miss the later plot (not a bridge so don't need PlotAlternativeTrackRouteGraphic)
1614  Display->Update();
1615  // need to keep this since Update() not called for PlotSmallOutput as too slow
1616  Utilities->CallLogPop(659);
1617  return;
1618  }
1619  // above covers for exiting at continuation, need XLinkPos check to exclude entering at a continuation
1620  if(LeadElement > -1)
1621  {
1623  // changed to lead so reset early
1624  {
1625  Track->TrackElementAt(225, LeadElement).Attribute = 0; // red
1627  // don't plot if zoomed out
1628  if(!Display->ZoomOutFlag)
1630  // covers signal resetting in same direction
1631  }
1632  }
1634  {
1635  if(AllRoutes->GetRouteTypeAndGraphics(5, LagElement, LagEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::AutoSigsRoute)
1636  {
1637  Display->PlotOutput(23, Track->TrackElementAt(227, LagElement).HLoc * 16, Track->TrackElementAt(228, LagElement).VLoc * 16, EXGraphicPtr);
1638  Display->PlotOutput(24, Track->TrackElementAt(229, LagElement).HLoc * 16, Track->TrackElementAt(230, LagElement).VLoc * 16, EntryDirectionGraphicPtr);
1639  TPrefDirElement PrefDirElement;
1640  // plot locked route marker for same side signal if appropriate (may be covered above but leave in), but only when train leaves element completely as this is a 16x16 graphic
1642  {
1644  RailGraphics->LockedRouteCancelPtr[PrefDirElement.GetELink()]);
1645  }
1647  LockedVectorNumber)))
1648  {
1650  }
1651  }
1652  }
1653  else if((LeadElement > -1) && (Track->TrackElementAt(233, LeadElement).TrackType == SignalPost))
1654  {
1655  Track->TrackElementAt(234, LeadElement).Attribute = 0; // red
1657  // don't plot if zoomed out
1658  if(!Display->ZoomOutFlag)
1660  // covers signal passed in opposite direction - replot as red, regardless of what it was before, though should already have been red
1661  }
1663  {
1664  if(AllRoutes->GetRouteTypeAndGraphics(6, LagElement, LagEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::AutoSigsRoute)
1665  {
1666  Display->PlotOutput(26, Track->TrackElementAt(236, LagElement).HLoc * 16, Track->TrackElementAt(237, LagElement).VLoc * 16, EXGraphicPtr);
1667  Display->PlotOutput(27, Track->TrackElementAt(238, LagElement).HLoc * 16, Track->TrackElementAt(239, LagElement).VLoc * 16, EntryDirectionGraphicPtr);
1668  // below added at v1.3.0 to reset signals if back out of an autosigs route under signaller control after changing direction, when new LeadElement not on route (if it had
1669  // been the route would have been ForceCancelled). Note that the signal is not facing the direction of travel else would have entered
1670  // "if(Track->TrackElementAt(, LagElement).Config[LagExitPos] == Signal)" above and wouldn't be here
1671  int RouteNumber;
1673  // already know it's an autosigsroute, this is just to get the RouteNumber
1674  // addition below at v1.3.2 - found that a signal that had reached double yellow in ContinuationAutoSigs was reset to red when a following train's lag element
1675  // moved off a signal in the normal course of events. It was caused when a train backed out of an autosigs route under signaller control after changing
1676  // direction (see DevHistory.txt). Hence check that the train is in signaller mode and that the train's lead element isn't on the same route before calling SetRouteSignals.
1677  int RouteNumber2;
1679  // already know it's an autosigsroute, this is just to get the RouteNumber
1680  if((TrainMode == Signaller) && (RouteNumber2 != RouteNumber))
1681  // note that if not in a route (as likely) then RouteNumber2 set to -1 )
1682  {
1683  AllRoutes->GetFixedRouteAt(217, RouteNumber).SetRouteSignals(10);
1684  // this was in the 1.3.0 addition but without the condition
1685  }
1686  // end of 1.3.2 addition
1687  // end of 1.3.0.addition
1688  }
1689  TPrefDirElement PrefDirElement;
1690  // plot locked route marker for opp side signal if appropriate (may be covered above but leave in), but only when train leaves element completely as this is a 16x16 graphic (OK - Straddle == LeadMidLag)
1692  {
1694  RailGraphics->LockedRouteCancelPtr[PrefDirElement.GetELink()]);
1695  }
1696  }
1697  }
1698  }
1699 
1700  // straddle ONLY changed here, check if 'LeadMid' first & if so ready for updating Elements
1701  if(Straddle == LeadMid)
1702  {
1703  AllowedToPassRedSignal = false;
1704  // if had been allowed to pass then at this point it will move half onto signal so can be reset
1705  // if(LagElement > -1) ResetTrainElementID(LagElement, LagEntryPos);//train fully off old LagElement so can clear TrainOnElement flags - no, reset at earlier call when lag moves off element
1706  if(DerailPending)
1707  // set during last GetLeadElement, but only act on it when train fully on offending point
1708  // i.e. next time Straddle reaches LeadMid
1709  {
1710  Derailed = true;
1711  DerailPending = false;
1715  Utilities->CallLogPop(657);
1716  return;
1717  }
1724  Straddle = MidLag;
1725  // train now fully on the updated Lag & Mid, the front segment is going to move onto the new
1726  // LeadElement during this function (note that if stopped at signal then won't get this far)
1727  if(LeadElement > -1)
1728  {
1730  // i.e an exit continuation only
1731  // if don't exclude entry continuations then can't progress past it
1732  {
1733  LeadElement = -1;
1734  }
1735  else
1736  {
1737  GetLeadElement(0);
1738  // sets or resets DerailPending & StoppedAtSignal, and sets LeadElement values
1740  if(Stopped())
1741  {
1742  if(TrainFailurePending) // ok, stopped so PlotElements set
1743  {
1744  TrainHasFailed(6);
1745  }
1746  Utilities->CallLogPop(658);
1747  return; // i.e. don't move forward one step if next element is a red signal
1748  }
1749  }
1750  }
1751  }
1752 
1753  if(LagElement > -1)
1754  {
1755  // below are the actions required at both half moves for LagElement > -1
1757 
1758  // if was in locked route but has timed out when train leaves then plot the normal track graphic over the route graphic that is
1759  // still in BackgroundGraphic[3], if wasn't in a route then will just replot the same BackgroundGraphic
1760  // need to do this for each half element
1761 
1762  TPrefDirElement PrefDirElement;
1763  if(!(AllRoutes->IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(7, LagElement, LagExitPos, PrefDirElement, LockedVectorNumber)))
1764  {
1765  int RouteNumber; // holder for call below - not used
1767  {
1768  if(Utilities->clTransparent == TColor(0xFFFFFF))
1769  // change to black for a white background
1770  {
1772  // only applies for AutoSigs Route in case was locked & timed out
1773  }
1774  else
1775  // change to white for a dark background
1776  {
1778  // only applies for AutoSigs Route in case was locked & timed out
1779  }
1781  }
1782  }
1783 
1785  // above in case train just moving off a bridge & either alternative track in a route - need to keep its route colour,
1786  // or a train on the opposite track - needs to be replotted
1787  }
1788 
1789  // update all array values
1790  HOffset[3] = HOffset[2];
1791  HOffset[2] = HOffset[1];
1792  HOffset[1] = HOffset[0];
1793  VOffset[3] = VOffset[2];
1794  VOffset[2] = VOffset[1];
1795  VOffset[1] = VOffset[0];
1796  Graphics::TBitmap *TempPtr = BackgroundPtr[3];
1797 
1798  BackgroundPtr[3] = BackgroundPtr[2];
1799  BackgroundPtr[2] = BackgroundPtr[1];
1800  BackgroundPtr[1] = BackgroundPtr[0];
1801  BackgroundPtr[0] = TempPtr;
1802 
1803  // update headcode graphics depending on Lead entry value
1804  if(LeadElement > -1) // if Lead is -1 then stays as is
1805  {
1807  {
1808  for(int x = 0; x < 4; x++)
1809  {
1810  HeadCodePosition[x] = HeadCodeGrPtr[3 - x];
1811  }
1812  }
1813  else
1814  {
1815  for(int x = 0; x < 4; x++)
1816  {
1818  }
1819  }
1820  }
1821 
1822  if(TrainMode == Timetable)
1823  {
1825  }
1826  else
1827  {
1829  }
1831 
1832  // plot new seg [0] on Lead & [2] on Mid ([2] always on Mid)
1833  if(LeadElement > -1)
1834  {
1835  if(Straddle == MidLag)
1836  // just about to move half onto the new lead element
1837  {
1839  // pick up new background bitmap [0]
1841  int LeadElementTrainID = Track->TrackElementAt(244, LeadElement).TrainIDOnElement;
1842  if((LeadElementTrainID > -1) && (LeadElementTrainID != TrainID))
1843  // check if own ID for entry at continuation, else crashes into itself!
1844  {
1845  // OK if crossing on a bridge
1846  int OtherTrainEntryPos = TrainController->EntryPos(0, LeadElementTrainID, LeadElement);
1847  if(OtherTrainEntryPos == -1)
1848  {
1849  throw Exception("Error - OtherTrainEntryPos not set");
1850  }
1851  if((Track->TrackElementAt(246, LeadElement).TrackType != Bridge) || (LeadEntryPos == OtherTrainEntryPos) ||
1852  // LeadEntryPos for rear end crashes
1853  (LeadExitPos == OtherTrainEntryPos))
1854  // LeadExitPos for head-on crashes
1855  {
1857  Crashed = true; // only set if Straddle = MidLag
1858  CallingOnFlag = false;
1859  // in case was set, need to disable call on if call on button had been pressed
1860  }
1861  }
1862  else if(MidElement > -1) // will be -1 for continuation entries
1863  {
1864  // check if about to move onto a crossing diagonal that is occupied by another train, and if so crash
1865  int MidExitLinkNum = Track->TrackElementAt(889, MidElement).Link[MidExitPos];
1866  int MidHLoc = Track->TrackElementAt(890, MidElement).HLoc;
1867  int MidVLoc = Track->TrackElementAt(891, MidElement).VLoc;
1868  int OtherTrainID = -1;
1869  if((MidExitLinkNum == 1) || (MidExitLinkNum == 3) || (MidExitLinkNum == 7) || (MidExitLinkNum == 9))
1870  {
1871  if(Track->DiagonalFouledByTrain(0, MidHLoc, MidVLoc, MidExitLinkNum, OtherTrainID))
1872  {
1873  TrainCrashedInto = OtherTrainID;
1874  Crashed = true; // only set if Straddle = MidLag
1875  CallingOnFlag = false;
1876  // in case was set, need to disable call on if call on button had been pressed
1877  }
1878  }
1879  }
1880  }
1881  else
1882  {
1884  // pick up new background bitmap [0]
1886  }
1887  PlotElement[0] = LeadElement;
1889  PlotTrainGraphic(12, 0, Display);
1890  }
1891 
1892  if(MidElement > -1)
1893  {
1894  PlotElement[2] = MidElement;
1896  PlotTrainGraphic(1, 2, Display);
1897  }
1898 
1899  // plot the new positions for [1] & [3] graphics - [1] on Mid if Straddle = MidLag, on Lead if Straddle = LeadMidLag
1900  // [3] on Lag if Straddle = MidLag, on Mid if Straddle = LeadMidLag
1901  if(Straddle == MidLag)
1902  {
1903  if(MidElement > -1)
1904  {
1905  PlotElement[1] = MidElement;
1907  PlotTrainGraphic(2, 1, Display);
1908  }
1909  if(LagElement > -1)
1910  {
1911  PlotElement[3] = LagElement;
1913  PlotTrainGraphic(3, 3, Display);
1914  }
1915  }
1916  else // Straddle == LeadMidLag
1917  {
1918  if(LeadElement > -1)
1919  {
1920  PlotElement[1] = LeadElement;
1922  PlotTrainGraphic(4, 1, Display);
1923  }
1924  if(MidElement > -1)
1925  {
1926  PlotElement[3] = MidElement;
1928  PlotTrainGraphic(5, 3, Display);
1929  }
1930  }
1931 
1932  if(Crashed)
1933  // only reach here if crash into another train, if crash into buffers or an LC then return earlier at the if(Stopped()) test
1934  {
1939  // in case was set, need to disable call on if call on button had been pressed
1946  Straddle = LeadMidLag;
1947  // was MidLag but plotted as LeadMidLag so change Straddle accordingly
1948  Display->Update();
1949  // resurrected when Update() dropped from PlotOutput etc
1950  Utilities->CallLogPop(660);
1951  return;
1952  }
1953 
1954  // deal here with station stops & pass times after all replotting done but before Straddle changed
1955  if(TrainMode == Timetable)
1956  {
1957  if(Straddle == LeadMidLag)
1958  {
1959  if((LeadElement > -1) && (MidElement > -1) && !TimetableFinished)
1960  {
1961  // NameInTimetableBeforeCDT returns the number by which the train ActionVectorEntryPtr needs to be incremented
1962  // to point to the location arrival entry - before a change of direction
1963  AnsiString LocName = Track->TrackElementAt(249, LeadElement).ActiveTrackElementName;
1964  bool StopRequired = false;
1965  int TTVPos = NameInTimetableBeforeCDT(1, LocName, StopRequired);
1966  if(TTVPos > -1) // -1 if can't find it or if name is ""
1967  {
1968  // check if at buffers (no, dropped buffer check to allow to crash into buffers) or a through station stop,
1969  // or a station where next element contains a train or a stop signal, if so
1970  // stop now, note that for 2nd check, if next element is a bridge then will have stopped by now so no need
1971  // to test the actual track the train is on since it can't be a platform
1972  TTrackElement LeadTrackElement = Track->TrackElementAt(258, LeadElement);
1973  TTrackElement NextTrackElement; // default for now
1974  bool TrainAtStopLinkPos1 = (LeadTrackElement.StationEntryStopLinkPos1 == LeadEntryPos);
1975  bool TrainAtStopLinkPos2 = (LeadTrackElement.StationEntryStopLinkPos2 == LeadEntryPos);
1976  bool ForwardConnection = (LeadTrackElement.Conn[LeadExitPos] > -1);
1977  int NextElementEntryPos = -1;
1978  int NextElementExitPos = -1;
1979  bool TrainOnNextElement = false;
1980  bool StopSignalAtNextElement = false;
1981  if(ForwardConnection)
1982  // if no forward connection can't derive anything from it without errors
1983  {
1984  NextTrackElement = Track->TrackElementAt(262, LeadTrackElement.Conn[LeadExitPos]);
1985  NextElementEntryPos = LeadTrackElement.ConnLinkPos[LeadExitPos];
1986  NextElementExitPos = Track->GetNonPointsOppositeLinkPos(NextElementEntryPos);
1987  // this is only for signals so no need to worry about points ambiguity
1988  TrainOnNextElement = (NextTrackElement.TrainIDOnElement > -1);
1989  StopSignalAtNextElement = ((NextTrackElement.Config[NextElementExitPos] == Signal) && (NextTrackElement.Attribute == 0));
1990  }
1991  // logic here is: if(train@stoplinkpos1 || train@stoplinkpos2 || (forward connection && (train on next element || stop signal at next element)))
1992  if(TrainAtStopLinkPos1 || TrainAtStopLinkPos2 || (ForwardConnection && (TrainOnNextElement || StopSignalAtNextElement)))
1993  {
1994  if(TTVPos > 0)
1995  {
1997  ActionVectorEntryPtr += TTVPos;
1998  }
1999  if(StopRequired)
2000  {
2001  StoppedAtLocation = true;
2002  StoppedAtSignal = false;
2003  // may have been set earlier at line 925 so need to reset as
2004  // StoppedAtLocation takes precedence and don't want both set at same time or have flashing graphic
2005  // in zoom out mode
2006  if(!TrainFailed)
2007  {
2009  // pale green
2010  }
2013  {
2014  TimeTimeLocArrived = true;
2015  // used in case of later signaller control, when need to know
2016  // whether had arrived or not, to avoid sending the arrival
2017  // message twice, see TInterface::TimetableControl1Click
2018  }
2019  }
2020  else
2021  {
2023  }
2025  {
2027  }
2028  // don't alter ActionVectorEntryPtr if at a TimeTimeLoc (& can't be anything else other than TimeLoc or PassTime after calling NameInTimetableBeforeCDT successfully)
2030  }
2031  }
2032  }
2033  }
2034  }
2035 
2036  if(Straddle == MidLag)
2037  {
2038  Straddle = LeadMidLag;
2039  FirstHalfMove = false;
2040  }
2041  else if(Straddle == LeadMidLag)
2042  {
2043  Straddle = LeadMid;
2044  FirstHalfMove = true;
2045  }
2046  else if(Straddle == LeadMid)
2047  {
2048  throw Exception("Error, Straddle shouldn't be LeadMid prior to resetting at exit from UpdateTrain");
2049  }
2050 
2051  if(TrainFailurePending) // ok, moving but PlotElements set above
2052  {
2053  TrainHasFailed(7);
2054  }
2055  Display->Update();
2056  // need to keep this since Update() not called for PlotSmallOutput as too slow
2057  Utilities->CallLogPop(661);
2058 }
2059 
2060 // ----------------------------------------------------------------------------
2061 
2062 Graphics::TBitmap *TTrain::SetOneGraphicCode(char CodeChar)
2063 {
2064  switch(CodeChar)
2065  {
2066  case '0':
2067  return RailGraphics->Code0;
2068 
2069  case '1':
2070  return RailGraphics->Code1;
2071 
2072  case '2':
2073  return RailGraphics->Code2;
2074 
2075  case '3':
2076  return RailGraphics->Code3;
2077 
2078  case '4':
2079  return RailGraphics->Code4;
2080 
2081  case '5':
2082  return RailGraphics->Code5;
2083 
2084  case '6':
2085  return RailGraphics->Code6;
2086 
2087  case '7':
2088  return RailGraphics->Code7;
2089 
2090  case '8':
2091  return RailGraphics->Code8;
2092 
2093  case '9':
2094  return RailGraphics->Code9;
2095 
2096  case 'A':
2097  return RailGraphics->CodeA;
2098 
2099  case 'B':
2100  return RailGraphics->CodeB;
2101 
2102  case 'C':
2103  return RailGraphics->CodeC;
2104 
2105  case 'D':
2106  return RailGraphics->CodeD;
2107 
2108  case 'E':
2109  return RailGraphics->CodeE;
2110 
2111  case 'F':
2112  return RailGraphics->CodeF;
2113 
2114  case 'G':
2115  return RailGraphics->CodeG;
2116 
2117  case 'H':
2118  return RailGraphics->CodeH;
2119 
2120  case 'I':
2121  return RailGraphics->CodeI;
2122 
2123  case 'J':
2124  return RailGraphics->CodeJ;
2125 
2126  case 'K':
2127  return RailGraphics->CodeK;
2128 
2129  case 'L':
2130  return RailGraphics->CodeL;
2131 
2132  case 'M':
2133  return RailGraphics->CodeM;
2134 
2135  case 'N':
2136  return RailGraphics->CodeN;
2137 
2138  case 'O':
2139  return RailGraphics->CodeO;
2140 
2141  case 'P':
2142  return RailGraphics->CodeP;
2143 
2144  case 'Q':
2145  return RailGraphics->CodeQ;
2146 
2147  case 'R':
2148  return RailGraphics->CodeR;
2149 
2150  case 'S':
2151  return RailGraphics->CodeS;
2152 
2153  case 'T':
2154  return RailGraphics->CodeT;
2155 
2156  case 'U':
2157  return RailGraphics->CodeU;
2158 
2159  case 'V':
2160  return RailGraphics->CodeV;
2161 
2162  case 'W':
2163  return RailGraphics->CodeW;
2164 
2165  case 'X':
2166  return RailGraphics->CodeX;
2167 
2168  case 'Y':
2169  return RailGraphics->CodeY;
2170 
2171  case 'Z':
2172  return RailGraphics->CodeZ;
2173 
2174  case 'a':
2175  return RailGraphics->Code_a;
2176 
2177  case 'b':
2178  return RailGraphics->Code_b;
2179 
2180  case 'c':
2181  return RailGraphics->Code_c;
2182 
2183  case 'd':
2184  return RailGraphics->Code_d;
2185 
2186  case 'e':
2187  return RailGraphics->Code_e;
2188 
2189  case 'f':
2190  return RailGraphics->Code_f;
2191 
2192  case 'g':
2193  return RailGraphics->Code_g;
2194 
2195  case 'h':
2196  return RailGraphics->Code_h;
2197 
2198  case 'i':
2199  return RailGraphics->Code_i;
2200 
2201  case 'j':
2202  return RailGraphics->Code_j;
2203 
2204  case 'k':
2205  return RailGraphics->Code_k;
2206 
2207  case 'l':
2208  return RailGraphics->Code_l;
2209 
2210  case 'm':
2211  return RailGraphics->Code_m;
2212 
2213  case 'n':
2214  return RailGraphics->Code_n;
2215 
2216  case 'o':
2217  return RailGraphics->Code_o;
2218 
2219  case 'p':
2220  return RailGraphics->Code_p;
2221 
2222  case 'q':
2223  return RailGraphics->Code_q;
2224 
2225  case 'r':
2226  return RailGraphics->Code_r;
2227 
2228  case 's':
2229  return RailGraphics->Code_s;
2230 
2231  case 't':
2232  return RailGraphics->Code_t;
2233 
2234  case 'u':
2235  return RailGraphics->Code_u;
2236 
2237  case 'v':
2238  return RailGraphics->Code_v;
2239 
2240  case 'w':
2241  return RailGraphics->Code_w;
2242 
2243  case 'x':
2244  return RailGraphics->Code_x;
2245 
2246  case 'y':
2247  return RailGraphics->Code_y;
2248 
2249  case 'z':
2250  return RailGraphics->Code_z;
2251 
2252  default:
2253  return RailGraphics->TempHeadCode;
2254  }
2255 }
2256 
2257 // ----------------------------------------------------------------------------
2258 
2259 void TTrain::SetHeadCodeGraphics(int Caller, AnsiString Code)
2260 {
2261  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetHeadCodeGraphics," + HeadCode);
2262  if(Code.Length() != 4)
2263  TrainController->StopTTClockMessage(62, "Headcode Incorrect length");
2264  for(int x = 1; x < 5; x++) // AnsiString indices start at 1
2265  {
2266  HeadCodeGrPtr[x - 1]->Assign(SetOneGraphicCode(Code[x]));
2267  }
2268  if(BackgroundColour != clB5G5R5)
2269  // i.e. not the basic graphic colour as loaded from resource file
2270  {
2271  for(int x = 0; x < 4; x++)
2272  {
2274  }
2275  }
2276  Utilities->CallLogPop(1484);
2277 }
2278 
2279 // ----------------------------------------------------------------------------
2280 
2281 void TTrain::GetLeadElement(int Caller)
2282  // assumes Mid & Lag already set, sets LeadElement,
2283  // LeadEntryPos, LeadExitPos & DerailPending (don't want to act on it immediately)
2284 {
2285  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetLeadElement," + HeadCode);
2286  DerailPending = false;
2290  {
2291  // attr 0=straight, - links 0 & 1 (0 = lead)
2292  // attr 1=diverging, - links 2 & 3 (2 = lead)
2293  // set appropriate next element or derail - use a subroutine & return element & bool for derail
2294  // points always have links 0 & 2 = lead, link 1 = trailing straight, link 3 = training diverging
2295 
2296  // if enter at lead, exit at whatever attr set at
2297  // if enter at lag, exit at lead, but set derail wrt attribute
2298  if((LeadEntryPos == 0) && (Track->TrackElementAt(272, LeadElement).Attribute == 0))
2299  LeadExitPos = 1;
2300 
2301  // strictly speaking shouldn't need to set to 0 and 2 correctly since TrackIsInARoute caters for both, but
2302  // best to be on safe side
2303  else if(LeadEntryPos == 0)
2304  {
2305  LeadEntryPos = 2;
2306  LeadExitPos = 3;
2307  }
2308  else if((LeadEntryPos == 2) && (Track->TrackElementAt(273, LeadElement).Attribute == 0))
2309  {
2310  LeadEntryPos = 0;
2311  LeadExitPos = 1;
2312  }
2313  else if(LeadEntryPos == 2)
2314  LeadExitPos = 3;
2315 
2316  else if((LeadEntryPos == 1) && (Track->TrackElementAt(274, LeadElement).Attribute == 0))
2317  LeadExitPos = 0;
2318  else if(LeadEntryPos == 1)
2319  {
2320  LeadExitPos = 0;
2321  DerailPending = true;
2322  }
2323  else if((LeadEntryPos == 3) && (Track->TrackElementAt(275, LeadElement).Attribute == 0))
2324  {
2325  LeadExitPos = 0;
2326  DerailPending = true;
2327  }
2328  else if(LeadEntryPos == 3)
2329  LeadExitPos = 0;
2330  }
2331  else if(LeadEntryPos == 0)
2332  LeadExitPos = 1;
2333  else if(LeadEntryPos == 1)
2334  LeadExitPos = 0;
2335  else if(LeadEntryPos == 2)
2336  LeadExitPos = 3;
2337  else if(LeadEntryPos == 3)
2338  LeadExitPos = 2;
2339  // TTrackElement TrackElement = Track->TrackElementAt(276, LeadElement);
2340 /* signal check moved to Update() function
2341  if((TrackElement.TrackType == SignalPost) && (TrackElement.Config[LeadExitPos] == Signal)
2342  && (TrackElement.Attribute == 0))//0 = red
2343  {
2344  StoppedAtSignal = true; //comment out for test of locked route graphic replot
2345  }
2346  else
2347  {
2348  StoppedAtSignal = false;
2349  }
2350 */
2351  Utilities->CallLogPop(662);
2352 }
2353 
2354 // ----------------------------------------------------------------------------
2355 
2356 void TTrain::GetOffsetValues(int Caller, int &HOffset, int &VOffset, int Link) const
2357 {
2358  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetOffsetValues," + AnsiString(Link) + "," + HeadCode);
2359  switch(Link)
2360  {
2361  case 1:
2362  {
2363  HOffset = 0;
2364  VOffset = 0;
2365  break;
2366  }
2367 
2368  case 2:
2369  {
2370  HOffset = 4;
2371  VOffset = 0;
2372  break;
2373  }
2374 
2375  case 3:
2376  {
2377  HOffset = 8;
2378  VOffset = 0;
2379  break;
2380  }
2381 
2382  case 4:
2383  {
2384  HOffset = 0;
2385  VOffset = 4;
2386  break;
2387  }
2388 
2389  case 6:
2390  {
2391  HOffset = 8;
2392  VOffset = 4;
2393  break;
2394  }
2395 
2396  case 7:
2397  {
2398  HOffset = 0;
2399  VOffset = 8;
2400  break;
2401  }
2402 
2403  case 8:
2404  {
2405  HOffset = 4;
2406  VOffset = 8;
2407  break;
2408  }
2409 
2410  case 9:
2411  {
2412  HOffset = 8;
2413  VOffset = 8;
2414  break;
2415  }
2416 
2417  default:
2418  {
2419  throw Exception("Error in GetOffsetValues - Link value wrong");
2420  }}
2421  Utilities->CallLogPop(674);
2422 }
2423 
2424 // ---------------------------------------------------------------------------
2425 
2426 bool TTrain::LowEntryValue(int EntryLink) const
2427 {
2428 /* returns true if EntryLink is 1, 2, 4 or 7, in these circumstances the front of the train (i.e.
2429  the character that is red or blue) is the last character of the headcode, otherwise it's the first character of the headcode
2430 */
2431  if((EntryLink == 1) || (EntryLink == 2) || (EntryLink == 4) || (EntryLink == 7))
2432  return true;
2433  else
2434  return false;
2435 }
2436 
2437 // ---------------------------------------------------------------------------
2438 
2439 void TTrain::PickUpBackgroundBitmap(int Caller, int HOffset, int VOffset, int Element, int EntryPos, Graphics::TBitmap *GraphicPtr) const
2440 {
2441  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PickUpBackgroundBitmap," + AnsiString(HOffset) + "," +
2442  AnsiString(VOffset) + "," + AnsiString(Element) + "," + AnsiString(EntryPos) + "," + HeadCode);
2443  Graphics::TBitmap *EXGraphicPtr = RailGraphics->bmTransparentBgnd;
2444  // default values
2445  Graphics::TBitmap *EntryDirectionGraphicPtr = RailGraphics->bmTransparentBgnd;
2446 
2447  TAllRoutes::TRouteType RouteType;
2448 
2449  RouteType = AllRoutes->GetRouteTypeAndGraphics(11, Element, EntryPos, EXGraphicPtr, EntryDirectionGraphicPtr);
2450 
2451  TRect SourceRect, DestRect;
2452 
2453  DestRect.init(0, 0, 8, 8); // initialise left, top, right, bottom
2454  // note right and bottom rect co-ordinates are 1 greater than the pixel area
2455  SourceRect.init(HOffset, VOffset, HOffset + 8, VOffset + 8);
2456  Graphics::TBitmap *TempGraphic = new Graphics::TBitmap;
2457 
2458  TempGraphic->PixelFormat = pf8bit;
2459  TempGraphic->Width = 16;
2460  TempGraphic->Height = 16;
2461  TTrackElement TempElement = Track->TrackElementAt(286, Element);
2462 
2463  if(TempElement.TrackType == Points)
2464  {
2465  TempGraphic->Assign(TempElement.GraphicPtr);
2466  TempGraphic->Transparent = true;
2467  TempGraphic->TransparentColor = Utilities->clTransparent;
2468  if(RouteType == TAllRoutes::AutoSigsRoute)
2469  {
2470  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2471  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2472  }
2473  else
2474  TempGraphic->Canvas->Draw(0, 0, Track->GetFilletGraphic(1, TempElement)); // add fillet
2475  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2476  }
2477  else if(TempElement.TrackType == GapJump) // plot set gap
2478  {
2479  if(TempElement.SpeedTag == 88)
2480  TempGraphic->Assign(RailGraphics->gl88set);
2481  else if(TempElement.SpeedTag == 89)
2482  TempGraphic->Assign(RailGraphics->gl89set);
2483  else if(TempElement.SpeedTag == 90)
2484  TempGraphic->Assign(RailGraphics->gl90set);
2485  else if(TempElement.SpeedTag == 91)
2486  TempGraphic->Assign(RailGraphics->gl91set);
2487  else if(TempElement.SpeedTag == 92)
2488  TempGraphic->Assign(RailGraphics->gl92set);
2489  else if(TempElement.SpeedTag == 93)
2490  TempGraphic->Assign(RailGraphics->bm93set);
2491  else if(TempElement.SpeedTag == 94)
2492  TempGraphic->Assign(RailGraphics->bm94set);
2493  else if(TempElement.SpeedTag == 95)
2494  TempGraphic->Assign(RailGraphics->gl95set);
2495  TempGraphic->Transparent = true;
2496  TempGraphic->TransparentColor = Utilities->clTransparent;
2497  if(RouteType == TAllRoutes::AutoSigsRoute)
2498  {
2499  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2500  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2501  }
2502  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2503  }
2504  // new for version 0.6
2505  else if(TempElement.TrackType == SignalPost)
2506  {
2507  if(TempElement.SigAspect == TTrackElement::GroundSignal)
2508  {
2509  for(int x = 0; x < 40; x++)
2510  {
2511  if((Track->SigTableGroundSignal[x].SpeedTag == TempElement.SpeedTag) && (Track->SigTableGroundSignal[x].Attribute == 0))
2512  // need to stop aspect
2513  {
2514  TempGraphic->Assign(Track->SigTableGroundSignal[x].SigPtr);
2515  break;
2516  }
2517  }
2518  }
2519  else // normal signal
2520  {
2521  TempGraphic->Assign(TempElement.GraphicPtr);
2522  // GraphicPtr set to normal signal in a signal track element
2523  }
2524  TempGraphic->Transparent = true;
2525  TempGraphic->TransparentColor = Utilities->clTransparent;
2526  if(RouteType == TAllRoutes::AutoSigsRoute)
2527  {
2528  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2529  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2530  }
2531  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2532  }
2533  else
2534  {
2535  // first check if there's a NamedNonStationLocation element at that position & if so pick up that as the background
2536  // can't name points gaps or signals so 'else' OK
2537  bool FoundFlag;
2538  TTrack::TIMPair IMPair = Track->GetVectorPositionsFromInactiveTrackMap(4, TempElement.HLoc, TempElement.VLoc, FoundFlag);
2539  if(FoundFlag)
2540  {
2542  {
2543  GraphicPtr->Canvas->CopyRect(DestRect, Track->InactiveTrackElementAt(26, IMPair.first).GraphicPtr->Canvas, SourceRect);
2544  TempGraphic->Assign(RailGraphics->bmName);
2545  TempGraphic->Transparent = true;
2546  TempGraphic->TransparentColor = Utilities->clTransparent;
2547  if(RouteType == TAllRoutes::AutoSigsRoute)
2548  {
2549  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2550  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2551  }
2552  else
2553  TempGraphic->Canvas->Draw(0, 0, TempElement.GraphicPtr);
2554  // draw track on top
2555  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2556  }
2557  else if(Track->InactiveTrackElementAt(116, IMPair.first).TrackType == LevelCrossing)
2558  {
2559  TempGraphic->Assign(TempElement.GraphicPtr);
2560  TempGraphic->Transparent = true;
2561  TempGraphic->TransparentColor = Utilities->clTransparent;
2562  // note that can't be an AutoSigsRoute
2563  // now overlay the LC central portion
2564  int BDVectorPos = -1; //not used
2565  if(Track->AnyLinkedBarrierDownVectorManual(0, Track->InactiveTrackElementAt(130, IMPair.first).HLoc, Track->InactiveTrackElementAt(131, IMPair.first).VLoc, BDVectorPos))
2566  {
2567  TempGraphic->Canvas->Draw(0, 0, RailGraphics->LCPlainMan);
2568  }
2569  else
2570  {
2571  TempGraphic->Canvas->Draw(0, 0, RailGraphics->LCPlain);
2572  }
2573  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2574  }
2575  else
2576  {
2577  TempGraphic->Assign(TempElement.GraphicPtr);
2578  TempGraphic->Transparent = true;
2579  TempGraphic->TransparentColor = Utilities->clTransparent;
2580  if(RouteType == TAllRoutes::AutoSigsRoute)
2581  {
2582  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2583  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2584  }
2585  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2586  }
2587  }
2588  else
2589  {
2590  TempGraphic->Assign(TempElement.GraphicPtr);
2591  TempGraphic->Transparent = true;
2592  TempGraphic->TransparentColor = Utilities->clTransparent;
2593  if(RouteType == TAllRoutes::AutoSigsRoute)
2594  {
2595  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2596  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2597  }
2598  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2599  }
2600  }
2601  delete TempGraphic;
2602  Utilities->CallLogPop(675);
2603 }
2604 
2605 // ---------------------------------------------------------------------------
2606 
2607 // This was an attempt to pick up the actual 8x8 graphic from the display, so that text & user graphics would show as soon as the train passed, and overwrite it with the
2608 // reconstructed track, and it works ok but for the little arrows showing route directions at start and end, which extend beyond the track. It doesn't matter for autosig
2609 // routes because they are replotted (alomg with the direction arrows) but for others they shouldn't be. Leave in in case an easy way to remove these pointers comes to mind.
2610 /*
2611 
2612  void TTrain::PickUpBackgroundBitmap(int Caller, int HOffset, int VOffset, int Element, int EntryPos, Graphics::TBitmap *GraphicPtr) const
2613  {
2614  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PickUpBackgroundBitmap," + AnsiString(HOffset) +
2615  "," + AnsiString(VOffset) + "," + AnsiString(Element) + "," + AnsiString(EntryPos) + "," + HeadCode);
2616  TAllRoutes::TRouteType RouteType;
2617  Graphics::TBitmap *EXGraphicPtr = RailGraphics->bmTransparentBgnd;
2618  // default values
2619  Graphics::TBitmap *EntryDirectionGraphicPtr = RailGraphics->bmTransparentBgnd;
2620  TTrackElement TempElement = Track->TrackElementAt(, Element); //this is a copy of the element passed into the function
2621  TRect SourceRect, DestRect, ScreenSourceRect;
2622  RouteType = AllRoutes->GetRouteTypeAndGraphics(7, Element, EntryPos, EXGraphicPtr, EntryDirectionGraphicPtr);
2623 
2624  DestRect.init(0, 0, 8, 8); //initialise left, top, right, bottom
2625  // note right and bottom rect co-ordinates are 1 greater than the pixel area
2626  SourceRect.init(HOffset, VOffset, HOffset + 8, VOffset + 8);
2627 
2628  //add text & user graphics if any to *GraphicPtr prior to adding the track
2629  int Left = ((TempElement.HLoc - Display->DisplayOffsetH) * 16) + HOffset;
2630  int Top = ((TempElement.VLoc - Display->DisplayOffsetV) * 16) + VOffset;
2631  int Right = Left + 8;
2632  int Bottom = Top + 8;
2633  ScreenSourceRect.init(Left, Top, Right, Bottom);
2634  GraphicPtr->Canvas->CopyMode = cmSrcCopy;
2635  GraphicPtr->Canvas->CopyRect(DestRect, Display->GetImage()->Canvas, ScreenSourceRect);
2636 
2637  Graphics::TBitmap *TempGraphic = new Graphics::TBitmap; //this will hold the 16x16 reconstructed element, prior to transfer of the 8x8 bit to *GraphicPtr
2638  TempGraphic->PixelFormat = pf8bit;
2639  TempGraphic->Width = 16;
2640  TempGraphic->Height = 16;
2641 
2642  Graphics::TBitmap *SourceGraphic = new Graphics::TBitmap; //this will hold the 8x8 element segment from TempGraphic - needed because to keep transparency have to use Draw, not CopyRect
2643  SourceGraphic->PixelFormat = pf8bit;
2644  SourceGraphic->Width = 16;
2645  SourceGraphic->Height = 16;
2646  SourceGraphic->Transparent = true;
2647  SourceGraphic->TransparentColor = Utilities->clTransparent;
2648 
2649  if (TempElement.TrackType == Points)
2650  {
2651  TempGraphic->Assign(TempElement.GraphicPtr);
2652  TempGraphic->Transparent = true;
2653  TempGraphic->TransparentColor = Utilities->clTransparent;
2654  if (RouteType == TAllRoutes::AutoSigsRoute)
2655  {
2656  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2657  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2658  }
2659  else
2660  {
2661  TempGraphic->Canvas->Draw(0, 0, Track->GetFilletGraphic(1, TempElement)); // add fillet
2662  }
2663  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2664  }
2665  else if (TempElement.TrackType == GapJump) // plot set gap
2666  {
2667  if (TempElement.SpeedTag == 88)
2668  TempGraphic->Assign(RailGraphics->gl88set);
2669  else if (TempElement.SpeedTag == 89)
2670  TempGraphic->Assign(RailGraphics->gl89set);
2671  else if (TempElement.SpeedTag == 90)
2672  TempGraphic->Assign(RailGraphics->gl90set);
2673  else if (TempElement.SpeedTag == 91)
2674  TempGraphic->Assign(RailGraphics->gl91set);
2675  else if (TempElement.SpeedTag == 92)
2676  TempGraphic->Assign(RailGraphics->gl92set);
2677  else if (TempElement.SpeedTag == 93)
2678  TempGraphic->Assign(RailGraphics->bm93set);
2679  else if (TempElement.SpeedTag == 94)
2680  TempGraphic->Assign(RailGraphics->bm94set);
2681  else if (TempElement.SpeedTag == 95)
2682  TempGraphic->Assign(RailGraphics->gl95set);
2683  TempGraphic->Transparent = true;
2684  TempGraphic->TransparentColor = Utilities->clTransparent;
2685  if (RouteType == TAllRoutes::AutoSigsRoute) {
2686  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2687  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2688  }
2689  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2690  }
2691  // new for version 0.6
2692  else if (TempElement.TrackType == SignalPost)
2693  {
2694  if (TempElement.SigAspect == TTrackElement::GroundSignal)
2695  {
2696  for (int x = 0; x < 40; x++)
2697  {
2698  if ((Track->SigTableGroundSignal[x].SpeedTag == TempElement.SpeedTag) && (Track->SigTableGroundSignal[x].Attribute == 0))
2699  // need to stop aspect
2700  {
2701  TempGraphic->Assign(Track->SigTableGroundSignal[x].SigPtr);
2702  break;
2703  }
2704  }
2705  }
2706  else // normal signal
2707  {
2708  TempGraphic->Assign(TempElement.GraphicPtr);
2709  // GraphicPtr set to normal signal in a signal track element
2710  }
2711  TempGraphic->Transparent = true;
2712  TempGraphic->TransparentColor = Utilities->clTransparent;
2713  if (RouteType == TAllRoutes::AutoSigsRoute) {
2714  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2715  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2716  }
2717  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2718  }
2719  else {
2720  // first check if there's a NamedNonStationLocation element at that position & if so pick up that as the background
2721  // can't name points gaps or signals so 'else' OK
2722  bool FoundFlag;
2723  TTrack::TIMPair IMPair = Track->GetVectorPositionsFromInactiveTrackMap
2724  (4, TempElement.HLoc, TempElement.VLoc, FoundFlag);
2725  if (FoundFlag)
2726  {
2727  if (Track->InactiveTrackElementAt(, IMPair.first).TrackType == NamedNonStationLocation)
2728  {
2729  GraphicPtr->Canvas->CopyRect(DestRect,
2730  Track->InactiveTrackElementAt(, IMPair.first).GraphicPtr->Canvas, SourceRect);
2731  TempGraphic->Assign(RailGraphics->bmName);
2732  TempGraphic->Transparent = true;
2733  TempGraphic->TransparentColor = Utilities->clTransparent;
2734  if (RouteType == TAllRoutes::AutoSigsRoute)
2735  {
2736  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2737  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2738  }
2739  else
2740  TempGraphic->Canvas->Draw(0, 0, TempElement.GraphicPtr);
2741  // draw track on top
2742  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas,
2743  SourceRect);
2744  }
2745  else if (Track->InactiveTrackElementAt(, IMPair.first).TrackType == LevelCrossing) {
2746  TempGraphic->Assign(TempElement.GraphicPtr);
2747  TempGraphic->Transparent = true;
2748  TempGraphic->TransparentColor = Utilities->clTransparent;
2749  // note that can't be an AutoSigsRoute
2750  // now overlay the LC central portion
2751  TempGraphic->Canvas->Draw(0, 0, RailGraphics->LCPlain);
2752  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas,
2753  SourceRect);
2754  }
2755  else {
2756  TempGraphic->Assign(TempElement.GraphicPtr);
2757  TempGraphic->Transparent = true;
2758  TempGraphic->TransparentColor = Utilities->clTransparent;
2759  if (RouteType == TAllRoutes::AutoSigsRoute) {
2760  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2761  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2762  }
2763  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas,
2764  SourceRect);
2765  }
2766  }
2767  else {
2768  TempGraphic->Assign(TempElement.GraphicPtr);
2769  TempGraphic->Transparent = true;
2770  TempGraphic->TransparentColor = Utilities->clTransparent;
2771  if (RouteType == TAllRoutes::AutoSigsRoute) {
2772  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2773  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2774  }
2775  SourceGraphic->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2776  GraphicPtr->Canvas->Draw(0, 0, SourceGraphic);
2777  }
2778  }
2779  delete TempGraphic;
2780  delete SourceGraphic;
2781  Utilities->CallLogPop();
2782  }
2783 */
2784 // ---------------------------------------------------------------------------
2785 
2786 void TTrain::PlotTrainGraphic(int Caller, int ArrayNumber, TDisplay *Disp)
2787 {
2788  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainGraphic," + AnsiString(ArrayNumber) + "," + HeadCode);
2789  if(PlotElement[ArrayNumber] == -1)
2790  {
2791  Utilities->CallLogPop(676);
2792  return; // not plotted yet
2793  }
2794  SetTrainElementID(0, PlotElement[ArrayNumber], PlotEntryPos[ArrayNumber]);
2795  // set before plot so gap flashing stops first
2796  Disp->PlotOutput(29, ((Track->TrackElementAt(295, PlotElement[ArrayNumber]).HLoc * 16) + HOffset[ArrayNumber]),
2797  ((Track->TrackElementAt(296, PlotElement[ArrayNumber]).VLoc * 16) + VOffset[ArrayNumber]), HeadCodePosition[ArrayNumber]);
2798  // Only need to set ID for leading element, stays set until train finally leaves the element
2799  Plotted = true;
2800  Utilities->CallLogPop(677);
2801 }
2802 
2803 // ---------------------------------------------------------------------------
2804 
2805 void TTrain::PlotBackgroundGraphic(int Caller, int ArrayNumber, TDisplay *Disp) const
2806 {
2807  Disp->PlotOutput(30, ((Track->TrackElementAt(297, PlotElement[ArrayNumber]).HLoc * 16) + HOffset[ArrayNumber]),
2808  ((Track->TrackElementAt(298, PlotElement[ArrayNumber]).VLoc * 16) + VOffset[ArrayNumber]), BackgroundPtr[ArrayNumber]);
2809 }
2810 
2811 // ---------------------------------------------------------------------------
2812 
2813 bool TTrain::BufferAtExit(int Caller, int Element, int ExitPos) const
2814 {
2815  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",BufferAtExit," + AnsiString(Element) + "," + AnsiString(ExitPos) + "," +
2816  HeadCode);
2817  if((Track->TrackElementAt(299, Element).TrackType == Buffers) && (Track->TrackElementAt(300, Element).Config[ExitPos] == End))
2818  {
2819  Utilities->CallLogPop(678);
2820  return true;
2821  }
2822  else
2823  {
2824  Utilities->CallLogPop(679);
2825  return false;
2826  }
2827 }
2828 
2829 // ---------------------------------------------------------------------------
2830 
2831 bool TTrain::ContinuationExit(int Caller, int Element, int ExitPos) const
2832 {
2833  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ContinuationExit," + AnsiString(Element) + "," + AnsiString(ExitPos) +
2834  "," + HeadCode);
2835  if((Track->TrackElementAt(301, Element).TrackType == Continuation) && (Track->TrackElementAt(302, Element).Config[ExitPos] == End))
2836  {
2837  Utilities->CallLogPop(680);
2838  return true;
2839  }
2840  else
2841  {
2842  Utilities->CallLogPop(681);
2843  return false;
2844  }
2845 }
2846 
2847 // ---------------------------------------------------------------------------
2848 
2849 bool TTrain::IsTrainIDOnBridgeTrackPos01(int Caller, unsigned int TrackVectorPosition)
2850  // test whether this train on a bridge on trackpos 0 & 1
2851 {
2852  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsTrainIDOnBridgeTrackPos01," + AnsiString(TrackVectorPosition) + "," +
2853  HeadCode);
2854  if(Track->TrackElementAt(303, TrackVectorPosition).TrackType != Bridge)
2855  {
2856  Utilities->CallLogPop(682);
2857  return false;
2858  }
2859  // if(Track->TrackElementAt(304, TrackVectorPosition).TrainIDOnElement != TrainID) return false; No, if a bridge could be one of 2 TrainIDs
2860  if(Track->TrackElementAt(305, TrackVectorPosition).TrainIDOnBridgeTrackPos01 == TrainID)
2861  {
2862  if(Track->TrackElementAt(306, TrackVectorPosition).TrainIDOnBridgeTrackPos23 == TrainID)
2863  {
2864  throw Exception("Error, same train on two different bridge tracks");
2865  }
2866  else
2867  {
2868  Utilities->CallLogPop(684);
2869  return true;
2870  }
2871  }
2872  else
2873  {
2874  Utilities->CallLogPop(685);
2875  return false;
2876  }
2877 }
2878 
2879 // ---------------------------------------------------------------------------
2880 
2881 bool TTrain::IsTrainIDOnBridgeTrackPos23(int Caller, unsigned int TrackVectorPosition)
2882  // test whether this train on a bridge on trackpos 2 & 3
2883 {
2884  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsTrainIDOnBridgeTrackPos23," + AnsiString(TrackVectorPosition) + "," +
2885  HeadCode);
2886  if(Track->TrackElementAt(307, TrackVectorPosition).TrackType != Bridge)
2887  {
2888  Utilities->CallLogPop(686);
2889  return false;
2890  }
2891  // if(Track->TrackElementAt(308, TrackVectorPosition).TrainIDOnElement != TrainID) return false; No, if a bridge could be one of 2 TrainIDs
2892  if(Track->TrackElementAt(309, TrackVectorPosition).TrainIDOnBridgeTrackPos23 == TrainID)
2893  {
2894  // don't carry out check for train on tracks 0 & 1 else will enter an infinite loop if train on both
2895  Utilities->CallLogPop(687);
2896  return true;
2897  }
2898  else
2899  {
2900  Utilities->CallLogPop(688);
2901  return false;
2902  }
2903 }
2904 
2905 // ---------------------------------------------------------------------------
2906 
2907 void TTrain::SetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
2908 {
2909  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetTrainElementID," + AnsiString(TrackVectorPosition) + "," +
2910  AnsiString(EntryPos) + "," + HeadCode);
2911  Track->TrackElementAt(310, TrackVectorPosition).TrainIDOnElement = TrainID;
2912 
2913  // unplot GapFlash graphics if land on flashing gap (this done before train plotted - see PlotTrainGraphic)
2914  if(Track->GapFlashFlag)
2915  {
2917  {
2920  Track->GapFlashFlag = false;
2921  }
2922  }
2923 
2924  if(Track->TrackElementAt(311, TrackVectorPosition).TrackType == Bridge)
2925  {
2926  if(EntryPos == -1)
2927  {
2928  throw Exception("Error, TrackVectorPosition set but not EntryPos in SetTrainElementID");
2929  }
2930  if(EntryPos < 2)
2931  Track->TrackElementAt(312, TrackVectorPosition).TrainIDOnBridgeTrackPos01 = TrainID;
2932  else
2933  Track->TrackElementAt(313, TrackVectorPosition).TrainIDOnBridgeTrackPos23 = TrainID;
2934  }
2935  Utilities->CallLogPop(690);
2936 }
2937 
2938 // ---------------------------------------------------------------------------
2939 
2940 void TTrain::ResetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
2941 {
2942  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ResetTrainElementID," + AnsiString(TrackVectorPosition) + "," +
2943  AnsiString(EntryPos) + "," + HeadCode);
2944  if(Track->TrackElementAt(314, TrackVectorPosition).TrackType != Bridge)
2945  {
2946  Track->TrackElementAt(315, TrackVectorPosition).TrainIDOnElement = -1;
2947  }
2948  else
2949  {
2950  if(EntryPos == -1)
2951  {
2952  throw Exception("Error, TrackVectorPosition set but not EntryPos in ResetTrainElementID");
2953  }
2954  if(EntryPos < 2)
2955  Track->TrackElementAt(316, TrackVectorPosition).TrainIDOnBridgeTrackPos01 = -1;
2956  else
2957  Track->TrackElementAt(317, TrackVectorPosition).TrainIDOnBridgeTrackPos23 = -1;
2958  if((EntryPos < 2) && (Track->TrackElementAt(318, TrackVectorPosition).TrainIDOnBridgeTrackPos23 > -1))
2959  // i.e. other train on track 2&3
2960  {
2961  Track->TrackElementAt(319, TrackVectorPosition).TrainIDOnElement = Track->TrackElementAt(320, TrackVectorPosition).TrainIDOnBridgeTrackPos23;
2962  }
2963  else if((EntryPos > 1) && (Track->TrackElementAt(321, TrackVectorPosition).TrainIDOnBridgeTrackPos01 > -1))
2964  // i.e. other train on track 1&2
2965  {
2966  Track->TrackElementAt(322, TrackVectorPosition).TrainIDOnElement = Track->TrackElementAt(323, TrackVectorPosition).TrainIDOnBridgeTrackPos01;
2967  }
2968  else
2969  Track->TrackElementAt(324, TrackVectorPosition).TrainIDOnElement = -1;
2970  }
2971  Utilities->CallLogPop(691);
2972 }
2973 
2974 // ---------------------------------------------------------------------------
2975 
2976 void TTrain::PlotAlternativeTrackRouteGraphic(int Caller, unsigned int ElementVecNum, int ElementEntryPos, int HOffset, int VOffset, TStraddle StraddleValue)
2977 {
2978  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotAlternativeTrackRouteGraphic," + AnsiString(ElementVecNum) + "," +
2979  AnsiString(ElementEntryPos) + "," + AnsiString(HOffset) + "," + AnsiString(VOffset) + "," + AnsiString(StraddleValue) + "," + HeadCode);
2980  int LockedVectorNumber;
2981 
2982  if(Track->TrackElementAt(325, ElementVecNum).TrackType != Bridge)
2983  // && (Track->TrackElementAt(326, ElementVecNum).TrackType != Crossover))
2984  { // only applies for a bridge as there can't be (or shouldn't be) 2 routes on an element that isn't a bridge
2985  Utilities->CallLogPop(692);
2986  return;
2987  }
2988  if(AllRoutes->TrackIsInARoute(0, ElementVecNum, (3 - ElementEntryPos)))
2989  // i.e other track is in a marked route
2990  // LinkPos doesn't have to be the entry position for the above check
2991  {
2992  TRect SourceRect, DestRect;
2993  DestRect.init(0, 0, 8, 8); // left, top, right, bottom
2994  // note right and bottom rect co-ordinates are 1 greater than the pixel area
2995  SourceRect.init(HOffset, VOffset, HOffset + 8, VOffset + 8);
2996  // identify the route element for the other track
2997  TAllRoutes::TRouteElementPair RoutePair1, RoutePair2;
2998  RoutePair1 = AllRoutes->GetRouteElementDataFromRoute2MultiMap(13, Track->TrackElementAt(327, ElementVecNum).HLoc,
2999  Track->TrackElementAt(328, ElementVecNum).VLoc, RoutePair2);
3000  int FirstELink, SecondELink = -1;
3001  FirstELink = AllRoutes->GetFixedRouteAt(149, RoutePair1.first).GetFixedPrefDirElementAt(159, RoutePair1.second).GetELink();
3002  // must be at least one
3003  if(RoutePair2.first > -1)
3004  SecondELink = AllRoutes->GetFixedRouteAt(150, RoutePair2.first).GetFixedPrefDirElementAt(160, RoutePair2.second).GetELink();
3005  TPrefDirElement RouteElement;
3006  // Graphics::TBitmap *RouteGraphic;
3007  if(FirstELink == Track->TrackElementAt(329, ElementVecNum).Link[ElementEntryPos])
3008  // i.e. other track is in RoutePair2
3009  {
3010  if(SecondELink == -1)
3011  {
3012  throw Exception("Error - Second ELink should be set but isn't in PlotAlternativeTrackRouteGraphic [1]");
3013  }
3014  if(SecondELink == Track->TrackElementAt(330, ElementVecNum).Link[ElementEntryPos])
3015  // error if both have same Link number
3016  {
3017  throw Exception("Error - First & Second ELinks have same value in PlotAlternativeTrackRouteGraphic");
3018  }
3019  // RouteGraphic = AllRoutes->GetFixedRouteAt(151, RoutePair2.first).GetFixedPrefDirElementAt(161, RoutePair2.second).GetEXGraphicPtr();
3020  RouteElement = AllRoutes->GetFixedRouteAt(152, RoutePair2.first).GetFixedPrefDirElementAt(162, RoutePair2.second);
3021  }
3022  else // other track is in RoutePair1
3023  {
3024  // RouteGraphic = AllRoutes->GetFixedRouteAt(153, RoutePair1.first).GetFixedPrefDirElementAt(163, RoutePair1.second).GetEXGraphicPtr();
3025  RouteElement = AllRoutes->GetFixedRouteAt(154, RoutePair1.first).GetFixedPrefDirElementAt(164, RoutePair1.second);
3026  }
3027  Graphics::TBitmap *DestGraphic = new Graphics::TBitmap;
3028  DestGraphic->PixelFormat = pf8bit;
3029  DestGraphic->Width = 8;
3030  DestGraphic->Height = 8;
3031  DestGraphic->Transparent = true;
3032  // has to be transparent or will overwrite the track that the train has just left
3033  DestGraphic->TransparentColor = Utilities->clTransparent;
3034  DestGraphic->Canvas->CopyRect(DestRect, RouteElement.GetRouteEXGraphicPtr()->Canvas, SourceRect);
3035  Display->PlotOutput(31, (Track->TrackElementAt(331, ElementVecNum).HLoc * 16) + HOffset,
3036  (Track->TrackElementAt(332, ElementVecNum).VLoc * 16) + VOffset, DestGraphic);
3037  // plot locked route marker for other route if appropriate
3038  TPrefDirElement PrefDirElement; // holder for next call, unused
3039  // plot locked route marker if appropriate, but only when train leaves element completely as this is a 16x16 graphic
3040  if(StraddleValue == LeadMidLag)
3041  {
3043  PrefDirElement, LockedVectorNumber))
3044  {
3045  Display->PlotOutput(32, (Track->TrackElementAt(333, ElementVecNum).HLoc * 16), (Track->TrackElementAt(334, ElementVecNum).VLoc * 16),
3046  RailGraphics->LockedRouteCancelPtr[RouteElement.GetELink()]);
3047  }
3048  }
3049  delete DestGraphic;
3050  }
3051  // but - there may be a train on the other track - if so need to replot it else the section of route overwrites it
3052  // also can only be a bridge or trains either have already or soon will crash
3053  if(Track->TrackElementAt(335, ElementVecNum).TrackType != Bridge)
3054  {
3055  Utilities->CallLogPop(695);
3056  return;
3057  }
3058  if(ElementEntryPos > 1) // other train is on track 01
3059  {
3060  if(Track->TrackElementAt(336, ElementVecNum).TrainIDOnBridgeTrackPos01 > -1)
3061  {
3063  }
3064  }
3065  else // other train is on track 23
3066  {
3067  if(Track->TrackElementAt(338, ElementVecNum).TrainIDOnBridgeTrackPos23 > -1)
3068  {
3070  }
3071  }
3072  Utilities->CallLogPop(696);
3073 }
3074 
3075 // ---------------------------------------------------------------------------
3076 
3077 void TTrain::CheckAndCancelRouteForWrongEndEntry(int Caller, int Element, int EntryPos)
3078 {
3079  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckAndCancelRouteForWrongEndEntry," + AnsiString(Element) + "," +
3080  AnsiString(EntryPos) + "," + HeadCode);
3081  int RouteNumber;
3082  bool WrongRoute = false;
3083  TPrefDirElement RouteElement;
3085  TAllRoutes::TRoute2MultiMapIterator Route2MultiMapIterator;
3086 
3087  if(AllRoutes->GetRouteTypeAndNumber(11, Element, EntryPos, RouteNumber) == TAllRoutes::NoRoute)
3088  // here if single track element & no route, or double track element with no route at EntryPos but still need to check if on points or a crossover on non-route track,
3089  // and force-erase route if so (bridge OK of course) note that GetRouteTypeAndNumber allows for points having an EntryPos of 0 or 2 & still returns correct values
3090  {
3091  if((Track->TrackElementAt(340, Element).TrackType == Crossover) || (Track->TrackElementAt(341, Element).TrackType == Points))
3092  {
3093  if(AllRoutes->GetRouteTypeAndNumber(12, Element, (3 - EntryPos), RouteNumber) != TAllRoutes::NoRoute)
3094  // (3-EntryPos) guarantees other route (0->3; 1->2; 2->1; 3->0)
3095  {
3096  if(AllRoutes->GetFixedRouteAt(179, RouteNumber).PrefDirSize() > 2)
3097  { // don't call for stub end routes
3099  }
3100  AllRoutes->GetModifiableRouteAt(13, RouteNumber).ForceCancelRoute(1);
3101  Utilities->CallLogPop(697);
3102  return;
3103  }
3104  }
3105  // also need to check for a route on a crossing diagonal
3106  TTrackElement TrackElement = Track->TrackElementAt(892, Element);
3107  int LinkNumber = TrackElement.Link[EntryPos];
3108  if((LinkNumber == 1) || (LinkNumber == 3) || (LinkNumber == 7) || (LinkNumber == 9))
3109  {
3110  if(AllRoutes->DiagonalFouledByRoute(0, TrackElement.HLoc, TrackElement.VLoc, LinkNumber))
3111  {
3112  // for LinkNumber = 1, potentially fouled diagonals are at H-1, V, Lk 3 & H, V-1, Lk 7
3113  bool LogActionErrorCalled = false;
3114  // to ensure only called once if have 2 routes on the 2 crossed diagonals
3115  if(LinkNumber == 1)
3116  {
3117  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(0, TrackElement.HLoc - 1, TrackElement.VLoc, 3, RouteNumber))
3118  {
3119  if(AllRoutes->GetFixedRouteAt(207, RouteNumber).PrefDirSize() > 2)
3120  { // don't call for stub end routes
3122  LogActionErrorCalled = true;
3123  }
3124  AllRoutes->GetModifiableRouteAt(20, RouteNumber).ForceCancelRoute(3);
3125  }
3126  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(1, TrackElement.HLoc, TrackElement.VLoc - 1, 7, RouteNumber))
3127  // not else in case have different routes on each diagonal, though shouldn't be possible
3128  {
3129  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(208, RouteNumber).PrefDirSize() > 2)
3130  { // don't call for stub end routes
3132  }
3133  AllRoutes->GetModifiableRouteAt(21, RouteNumber).ForceCancelRoute(4);
3134  }
3135  }
3136 
3137  // for LinkNumber = 3, potentially fouled diagonals are at H+1, V, Lk 1 & H, V-1 Lk 9
3138  else if(LinkNumber == 3)
3139  {
3140  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(2, TrackElement.HLoc + 1, TrackElement.VLoc, 1, RouteNumber))
3141  {
3142  if(AllRoutes->GetFixedRouteAt(209, RouteNumber).PrefDirSize() > 2)
3143  { // don't call for stub end routes
3145  LogActionErrorCalled = true;
3146  }
3147  AllRoutes->GetModifiableRouteAt(22, RouteNumber).ForceCancelRoute(5);
3148  }
3149  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(3, TrackElement.HLoc, TrackElement.VLoc - 1, 9, RouteNumber))
3150  // not else in case have different routes on each diagonal, though shouldn't be possible
3151  {
3152  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(210, RouteNumber).PrefDirSize() > 2)
3153  { // don't call for stub end routes
3155  }
3156  AllRoutes->GetModifiableRouteAt(23, RouteNumber).ForceCancelRoute(6);
3157  }
3158  }
3159 
3160  // for LinkNumber = 7, potentially fouled diagonals are at H-1, V, Lk 9 & H, V+1 Lk 1
3161  else if(LinkNumber == 7)
3162  {
3163  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(4, TrackElement.HLoc - 1, TrackElement.VLoc, 9, RouteNumber))
3164  {
3165  if(AllRoutes->GetFixedRouteAt(211, RouteNumber).PrefDirSize() > 2)
3166  { // don't call for stub end routes
3168  LogActionErrorCalled = true;
3169  }
3170  AllRoutes->GetModifiableRouteAt(24, RouteNumber).ForceCancelRoute(7);
3171  }
3172  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(5, TrackElement.HLoc, TrackElement.VLoc + 1, 1, RouteNumber))
3173  // not else in case have different routes on each diagonal, though shouldn't be possible
3174  {
3175  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(212, RouteNumber).PrefDirSize() > 2)
3176  { // don't call for stub end routes
3178  }
3179  AllRoutes->GetModifiableRouteAt(25, RouteNumber).ForceCancelRoute(8);
3180  }
3181  }
3182 
3183  // for LinkNumber = 9, potentially fouled diagonals are at H+1, V, Lk 7 & H, V+1 Lk 3
3184  else if(LinkNumber == 9)
3185  {
3186  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(6, TrackElement.HLoc + 1, TrackElement.VLoc, 7, RouteNumber))
3187  {
3188  if(AllRoutes->GetFixedRouteAt(213, RouteNumber).PrefDirSize() > 2)
3189  { // don't call for stub end routes
3191  LogActionErrorCalled = true;
3192  }
3193  AllRoutes->GetModifiableRouteAt(26, RouteNumber).ForceCancelRoute(9);
3194  }
3195  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(7, TrackElement.HLoc, TrackElement.VLoc + 1, 3, RouteNumber))
3196  // not else in case have different routes on each diagonal, though shouldn't be possible
3197  {
3198  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(214, RouteNumber).PrefDirSize() > 2)
3199  { // don't call for stub end routes
3201  }
3202  AllRoutes->GetModifiableRouteAt(27, RouteNumber).ForceCancelRoute(10);
3203  }
3204  }
3205  }
3206  }
3207  Utilities->CallLogPop(698);
3208  return; // no route on other track or no other track
3209  }
3210  // here if there is a route at Element & EntryPos - so there can't be a route on the other track if a 4 track element, unless it's a bridge & that's ok
3211  for(unsigned int x = 0; x < AllRoutes->GetFixedRouteAt(155, RouteNumber).PrefDirSize(); x++)
3212  {
3213  RouteElement = AllRoutes->GetFixedRouteAt(156, RouteNumber).GetFixedPrefDirElementAt(165, x);
3214  bool PointsAtElement = (Track->TrackElementAt(987, Element).TrackType == Points); // new after v2.4.1 for points check - Xeon repoted it 30/05/20. He found that for routes that
3215  if(RouteElement.GetTrackVectorPosition() == (unsigned int)Element) // cross bridges at both levels can have entrypos 0 & other exitpos 2 so if don't have this check can cancel a route wrongly
3216  {
3217  if(RouteElement.GetELinkPos() == EntryPos)
3218  {
3219  Utilities->CallLogPop(699);
3220  return; // right direction
3221  }
3222  else if((RouteElement.GetELinkPos() == 2) && (EntryPos == 0) && PointsAtElement)
3223  {
3224  Utilities->CallLogPop(700);
3225  return; // right direction (points)
3226  }
3227  else if((RouteElement.GetELinkPos() == 0) && (EntryPos == 2) && PointsAtElement)
3228  {
3229  Utilities->CallLogPop(701);
3230  return; // right direction (points)
3231  }
3232  else if(RouteElement.GetXLinkPos() == EntryPos)
3233  {
3234  WrongRoute = true;
3235  break; // wrong direction
3236  }
3237  else if((RouteElement.GetXLinkPos() == 2) && (EntryPos == 0) && PointsAtElement) // ok for bridges
3238  {
3239  WrongRoute = true;
3240  break; // wrong direction
3241  }
3242  else if((RouteElement.GetXLinkPos() == 0) && (EntryPos == 2) && PointsAtElement) // ok for bridges
3243  {
3244  WrongRoute = true;
3245  break; // wrong direction
3246  }
3247  }
3248  }
3249  if(!WrongRoute)
3250  {
3251  throw Exception("Error, Element in route but no route found in CheckAndCancelRouteForWrongEndEntry");
3252  }
3253  if(AllRoutes->GetFixedRouteAt(180, RouteNumber).PrefDirSize() > 2)
3254  { // don't call for stub end routes
3256  }
3257  AllRoutes->GetModifiableRouteAt(14, RouteNumber).ForceCancelRoute(2);
3258  Utilities->CallLogPop(703);
3259 }
3260 
3261 // ---------------------------------------------------------------------------
3262 
3263 void TTrain::PlotTrainWithNewBackgroundColour(int Caller, TColor NewBackgroundColour, TDisplay *Disp)
3264 {
3265  if(BackgroundColour == NewBackgroundColour)
3266  return; // don't replot if already correct
3267 
3268  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainWithNewBackgroundColour," + AnsiString(NewBackgroundColour));
3269  bool ColourError = false, ColourError2 = false;
3270 
3271  RailGraphics->ChangeBackgroundColour(1, FrontCodePtr, FrontCodePtr, NewBackgroundColour, BackgroundColour, ColourError);
3272  if(ColourError)
3273  ColourError2 = true;
3274  for(int x = 0; x < 4; x++)
3275  {
3276  RailGraphics->ChangeBackgroundColour(2, HeadCodeGrPtr[x], HeadCodeGrPtr[x], NewBackgroundColour, BackgroundColour, ColourError);
3277  if(ColourError)
3278  ColourError2 = true;
3279  }
3280  if(ColourError2)
3281  {
3283  "ERROR: Colour depth insufficient to display train colours properly. Please ensure that the 'safe' (web) palette of 256 colours can be displayed");
3284  }
3285 
3286  // NB need a separate 'for' loop since the plot order can be different from the graphic order depending on the direction
3287  // of motion
3288  for(int x = 0; x < 4; x++)
3289  {
3290  PlotTrainGraphic(6, x, Disp);
3291  }
3292  BackgroundColour = NewBackgroundColour;
3293  Display->Update();
3294  // need to keep this since Update() not called for PlotSmallOutput as too slow
3295  Utilities->CallLogPop(704);
3296 }
3297 
3298 // ---------------------------------------------------------------------------
3299 
3300 void TTrain::SetTrainMovementValues(int Caller, int TrackVectorPosition, int EntryPos)
3301 /*
3302 Note: Within the loop BrakeRate can only increase and MaxExitSpeed can only reduce
3303 
3304 Summary: Called during PlotStartPosition to set initial values, when stopped and need to restart, and during UpdateTrain when Straddle is LeadMidLag,
3305 i.e. just as the front of a train is about to move fully onto an element, where TrackVectorPosition is the element immediately in front
3306 of the element the front of the train is moving fully on to. The function calculates the times and speeds at the next half-element and
3307 full-element moves.
3308 
3309 Detail: TrackVectorPosition & EntryPos correspond to the TrackVector element immediately in front of where the train is at
3310 the end of the current Update(). EntrySpeed is needed but this is a class data member so isn't passed in. Set the
3311 train BrakeRate to zero (for now, likely to be altered later), & check if zero entry speed with another train directly in front & if so
3312 remain stopped. Pick up the half length value and speed limit for the EntryPos track, and set FrontElementLength to the length of the
3313 EntryPos track, then set LimitingSpeed to the minimum of the element speed limit or the train's maximum speed. Check if running past a
3314 red signal and set SPADFlag if so (use 1 for EntrySpeed rather than 0 as this value is a double so could be slightly in excess of 0).
3315 In this case set the brake rate to maximum to stop as soon as possible.
3316 
3317 For no SPAD calculate the distance that will be travelled at the maximum speed at which the train can exit the next element at half
3318 MaxBrakeRate, this is DistanceAtHalfBraking (also calculate DistanceAtThreeQuarterBraking - used for stopping under signaller control).
3319 DistanceAtHalfBraking is used as the limiting forward look from the next element (i.e. following EntryPos)
3320 for computing the actual braking rate. If no more restrictive speed limits or reasons to stop are found within the forward look then the
3321 train can accelerate or stay at its (local) maximum speed for the next element. The maximum speed on exit from the next element is used
3322 for calculating the forward look because it represents the worst case - i.e. assumes that the train accelerates for the next element.
3323 
3324 A loop is now entered where the CumulativeLength is updated and each successive element (if there are any - current element checked
3325 first to see whether buffers or continuation) in turn is examined: first the length of the
3326 current element is added to the cumulative length; then the half length and speedlimit are set for the next element - points are
3327 followed according to their current setting (Attribute), but derailments are ignored as these are dealt with outside this function; checks
3328 are then made to see whether the next element is a red signal (train should stop before it); next element is a buffer (train should stop
3329 at the end of it so the cumulative length has the next element length added); current element is a buffer (train should stop
3330 at the end of the current element so no need to alter the cumulative length); or have reached a named location stop position. For any of
3331 these reasons, or if stopping under signaller control, there is no more looping, instead the braking rate is calculated to bring the train
3332 to a stop over CumulativeLength. For all normal purposes the braking rate will be less than half (light braking), or less than three
3333 quarters if stopping under signaller control (heavy braking). However if signals are reset in front of a train then the train may need
3334 emergency braking (> 90% max brake rate) and a SPAD may result. Similarly if points are chaged in front of a train that divert it into a
3335 siding then again emeregency braking may be necessary and a crash may result.
3336 
3337 If the train is due to stop then the function calculates the half and full times and speeds and returns. However the calculation depends
3338 on the conditions at entry. If the EntrySpeed is lower than MaxHalfSpeed and the EntryPos element is the one
3339 that the train has to stop at the end of, as it might be for example if train had been stopped at a signal and the next element is a
3340 buffer, then the train accelerates for half the element and brakes for the other half.
3341 Now the BrakeRate is calculated (limited to the MaxBrakeRate), but if it is less than a value calculated at an earlier pass round
3342 the loop then it retains its earlier value (may be due to a close speed restriction that requires more braking than a more distant stop
3343 requirement). The MaxExitSpeedAtHalfBraking (maximum speed at which the train can leave the current element and still stop when required
3344 at half the max braking rate) value is also calculated using EntrySpeed and CumulativeLength, but limiting it to the line speed limit or
3345 train MaxRunningSpeed whichever is the lower. If EntrySpeed > MaxExitSpeedAtHalfBraking then braking is required, so the half and full
3346 speed and time values for the current element are calculated using BrakeRate, EntrySpeed and CurrentElementHalfLength. If need to stop
3347 at the end of the current elemecumulativent for other than a red signal (SPADs can occur) then ExitSpeedFull is set to 0. It should be calculated
3348 as 0 anyway for other than a red signal but this makes sure. If EntrySpeed <= MaxExitSpeedAtHalfBraking then can calculate the half and
3349 full speed and time values for acceleration over the current element, but limit ExitSpeedHalf & Full to MaxExitSpeedAtHalfBraking or to
3350 the current element speed limit if necessary. Check whether ExitSpeedHalf <= EntrySpeed (+0.01 since it's a double) and use constant speed
3351 time values for Half & Full if so, but prior to this increase EntrySpeed if necessary to avoid a divide by zero error.
3352 
3353 If the train is not due to stop within the DistanceAtHalfBraking from the next element following EntryPos then the next element (if there
3354 is one) is checked to see if its speed limit is less than the current value of LimitingSpeed (which is the minimum of any earlier element's
3355 speed limit that has been examined within the loop and the train's MaxRunning speed), and if so LimitingSpeed is set down to it. Now
3356 the MaxExitSpeedAtHalfBraking is calculated, limiting it to LimitingSpeed if less, in case need to accelerate in the current element, in
3357 which case the exit speeds need to be limited to MaxExitSpeedAtHalfBraking. If EntrySpeed > LimitingSpeed then calculate the braking rate
3358 to bring the speed down to LimitingSpeed in CumulativeLength, keeping the existing BrakeRate value if lower and keeping it within
3359 MaxBrakeRate.
3360 
3361 Then, providing the current element isn't a buffer or continuation, the 'Current' values are updated from the 'Next' values ready for
3362 the next loop iteration. The loop is broken out of if the current element is a buffer or continuation, the next element is a
3363 continuation, or (CumulativeLength - FrontElementLength) >= DistanceAtHalfBraking.
3364 
3365 Now the final Half and Full values can be set for braking (if BrakeRate > 0.01), or accelerating - limiting the half and full exit speed
3366 values to MaxExitSpeedAtHalfBraking if necessary, and using constant speed time values if the exit speeds aren't much different to
3367 EntrySpeed and EntrySpeed > 0.01 (to avoid a divide by zero error).
3368 
3369 Note that in no circumstances will a train stop when straddling 3 elements, it will always be fully on two elements. This is ensured
3370 by UpdateTrain() which never sets any stop conditions unless the train is fully on 2 elements when that function returns, i.e. entered
3371 when Straddle == LeadMidLag
3372 */
3373 {
3374  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetTrainMovementValues," + AnsiString(TrackVectorPosition) + "," +
3375  AnsiString(EntryPos) + "," + HeadCode);
3376  int EntryHalfLength, CurrentElementHalfLength, NextElementHalfLength, CumulativeLength = 0, CurrentTrackVectorPosition = TrackVectorPosition;
3377  int DistanceAtHalfBraking, DistanceAtThreeQuarterBraking, ExitPos, NextTrackVectorPosition, NextEntryPos;
3378  bool RedSignalFlag = false, BuffersFlag = false, StationFlag = false, BuffersOrContinuationNowFlag = false, ContinuationNextFlag = false,
3379  TrainInFrontInSignallerModeFlag = false;
3380  double LimitingSpeed, FrontElementMaxSpeed, MaxExitSpeedAtHalfBrakingSquared, MaxExitSpeedAtHalfBraking, NextSpeedLimit, TempBrakeRate;
3381  double ExitSpeedHalfSquared, ExitSpeedFullSquared;
3382  bool SignallerStopRequired = false;
3383 
3385  // set high to begin with to avoid divide by zero errors on restart after stops, will be set lower later
3386 
3387  // Member variables: EntrySpeed, ExitSpeedHalf, ExitSpeedFull, MaxExitSpeed, BrakeRate, EntryTime, ExitTimeHalf, ExitTimeFull, FrontElementSpeedLimit, FrontElementLength;
3388 
3389  OneLengthAccelDecel = false;
3390  BrakeRate = 0;
3391 
3392 //find FrontElementLength & FrontElementSpeedLimit (these correspond to TrackVectorPosition input value);
3393  if(CurrentTrackVectorPosition > -1)
3394  {
3395  if(Track->TrackElementAt(855, CurrentTrackVectorPosition).TrackType == Points) // this test & section added at v0.6
3396  {
3397  if((EntryPos == 0) || (EntryPos == 2))
3398  {
3399  if(Track->TrackElementAt(856, CurrentTrackVectorPosition).Attribute == 0)
3400  {
3401  CurrentElementHalfLength = (Track->TrackElementAt(857, CurrentTrackVectorPosition).Length01) / 2;
3402  FrontElementSpeedLimit = Track->TrackElementAt(858, CurrentTrackVectorPosition).SpeedLimit01;
3403  }
3404  else
3405  {
3406  CurrentElementHalfLength = (Track->TrackElementAt(859, CurrentTrackVectorPosition).Length23) / 2;
3407  FrontElementSpeedLimit = Track->TrackElementAt(860, CurrentTrackVectorPosition).SpeedLimit23;
3408  }
3409  }
3410  else if(EntryPos == 1)
3411  {
3412  CurrentElementHalfLength = (Track->TrackElementAt(861, CurrentTrackVectorPosition).Length01) / 2;
3413  FrontElementSpeedLimit = Track->TrackElementAt(862, CurrentTrackVectorPosition).SpeedLimit01;
3414  }
3415  else // == 3
3416  {
3417  CurrentElementHalfLength = (Track->TrackElementAt(863, CurrentTrackVectorPosition).Length23) / 2;
3418  FrontElementSpeedLimit = Track->TrackElementAt(864, CurrentTrackVectorPosition).SpeedLimit23;
3419  }
3420  }
3421  else
3422  {
3423  if(EntryPos > 1)
3424  {
3425  CurrentElementHalfLength = (Track->TrackElementAt(348, CurrentTrackVectorPosition).Length23) / 2;
3426  FrontElementSpeedLimit = Track->TrackElementAt(349, CurrentTrackVectorPosition).SpeedLimit23;
3427  }
3428  else
3429  {
3430  CurrentElementHalfLength = (Track->TrackElementAt(350, CurrentTrackVectorPosition).Length01) / 2;
3431  FrontElementSpeedLimit = Track->TrackElementAt(351, CurrentTrackVectorPosition).SpeedLimit01;
3432  }
3433  }
3434  EntryHalfLength = CurrentElementHalfLength;
3435  FrontElementLength = 2 * CurrentElementHalfLength;
3436  }
3437  else
3438  {
3439  throw Exception("Error - CurrentTrackVectorPosition < 0 in SetTrainMovementValues");
3440  }
3441  if((CurrentElementHalfLength < 0) || (FrontElementSpeedLimit < 0))
3442  {
3443  throw Exception("Error - HalfLength or SpeedLimit < 0 in SetTrainMovementValues");
3444  }
3445 
3446  // check if zero entry speed with another train directly in front & if so remain stopped
3447  if(Track->OtherTrainOnTrack(2, CurrentTrackVectorPosition, EntryPos, TrainID) && (EntrySpeed < 1))
3448  {
3449  EntrySpeed = 0;
3450  ExitSpeedHalf = 0;
3451  ExitSpeedFull = 0;
3452  MaxExitSpeed = 0;
3453  BrakeRate = 0;
3454  ExitTimeHalf = EntryTime + TDateTime(1/24); //set this high in case used later though unlikely
3455  ExitTimeFull = EntryTime + TDateTime(1/23); //set about 2.5 mins later than half time
3456  StoppedForTrainInFront = true;
3457  Utilities->CallLogPop(705);
3458  return;
3459  }
3460 
3461  // new at v2.4.0 - check for stopped and zero power
3462  if((EntrySpeed < 1) && PowerAtRail < 1)
3463  {
3464  EntrySpeed = 0;
3465  ExitSpeedHalf = 0;
3466  ExitSpeedFull = 0;
3467  MaxExitSpeed = 0;
3468  BrakeRate = 0;
3469  ExitTimeHalf = EntryTime + TDateTime(1/24); //set this high in case used later though unlikely
3470  ExitTimeFull = EntryTime + TDateTime(1/23); //set about 2.5 mins later than half time
3471  StoppedWithoutPower = true;
3472  Utilities->CallLogPop(2125);
3473  return;
3474  }
3475 
3476 //set LimitingSpeed & FrontElementMaxSpeed (internal values)
3477  if(BeingCalledOn)
3478  {
3479  LimitingSpeed = CallOnMaxSpeed;
3480  }
3481  else
3482  {
3483  LimitingSpeed = MaximumSpeedLimit;
3484  }
3485  if(LimitingSpeed > FrontElementSpeedLimit)
3486  LimitingSpeed = FrontElementSpeedLimit;
3487  if(LimitingSpeed > MaxRunningSpeed) //MaxRunningSpeed is set in AddTrain depending on timetable or signaller control mode
3488  LimitingSpeed = MaxRunningSpeed;
3489  FrontElementMaxSpeed = LimitingSpeed;
3490 
3491 /*
3492  for braking the deceleration rate is constant so the following formuli (Newton's Laws) are used:-
3493  (1) V^2/(3.6^2) = U^2/(3.6^2) - 2FS;
3494  (2) V/3.6 = U/3.6 - FT;
3495  (3) S = UT/3.6 - 0.5FT^2
3496  where(V = final speed in kph [km/h/3.6 = m/s], U = initial speed in km/h, F = deceleration rate in m/s/s, S = distance in m & T = time in secs)
3497 
3498  for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
3499  (4) V^2/(3.6^2) - U^2/(3.6^2) = A^2T;
3500  (5) V = 3.6 * ((1.5*S*A^2) + U^3/ (3.6)^3)^0.333334;
3501  where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph , S = distance in m & T = time in secs
3502  It's a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
3503 
3504  calc max speed that can attain on exit from next element (as could accelerate over next element) and use that speed to calc
3505  DistanceAtHalfBraking, if use actual speed may miss a stop requirement just outside look-ahead & accelerate, and at next calc
3506  be unable to stop or have hard acceleration followed immediately by hard braking, this speed makes for smoother operation
3507 */
3508 
3509 // check if running past a red signal without permission
3510  if((Track->TrackElementAt(352, CurrentTrackVectorPosition).Config[Track->GetNonPointsOppositeLinkPos(EntryPos)] == Signal) && (Track->TrackElementAt(353,
3511  CurrentTrackVectorPosition).Attribute == 0) && (EntrySpeed > 1) && !AllowedToPassRedSignal)
3512  {
3513  SPADFlag = true; // user has to intervene to reset & restart after spad
3514  }
3515 
3516  if(!SPADFlag)
3517  {
3518  ExitSpeedFull = 3.6 * Power(((3 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
3519 
3520  double ExitSpeedAtMaxBraking;
3521  // below introduced at v2.4.0, was ExitSpeedFull = LimitingSpeed; but that allowed very high brake rates when
3522  // took signaller control of a fast failed train with signaller limiting speed 30km/h
3524  {
3525  ExitSpeedAtMaxBraking = 0;
3526  }
3527  else
3528  {
3529  ExitSpeedAtMaxBraking = sqrt((EntrySpeed * EntrySpeed) - 2 * MaxBrakeRate * FrontElementLength);
3530  }
3531  double SpeedToUse;
3532  // use the highest of LimitingSpeed or ExitSpeedAtMaxBraking - added after v2.4.1 because trains entering at a continuation with zero (or very low) speed
3533  // & 2 elements before signal caused ExitSpeedAtMaxBraking & hence DistanceAtHalfBraking and DistanceAtThreeQuarterBraking to be zero, so no restriction was recognised
3534  // for first element & train accelerated at maximum rate, then at 2nd element train couldn't brake in time and overran the signal - notified by Micke via Discord on 02/06/20
3535  if(ExitSpeedAtMaxBraking > LimitingSpeed)
3536  {
3537  SpeedToUse = ExitSpeedAtMaxBraking;
3538  }
3539  else
3540  {
3541  SpeedToUse = LimitingSpeed;
3542  }
3543  if(ExitSpeedFull > SpeedToUse)
3544  {
3545  ExitSpeedFull = SpeedToUse;
3546  }
3547  DistanceAtHalfBraking = ExitSpeedFull * ExitSpeedFull / 3.6 / 3.6 / MaxBrakeRate;
3548  DistanceAtThreeQuarterBraking = ExitSpeedFull * ExitSpeedFull / 3.6 / 3.6 / 1.5 / MaxBrakeRate; // used for signaller stops
3549 
3550  //now enter a do loop to examine each element in turn from the front of the train to calc the cumulative length and to see if a stop is required (flag set if so -
3551  //RedSignalFlag, BuffersFlag, StationFlag, TrainInFrontInSignallerModeFlag, SignallerStopRequired, StepForwardFlag) in which case there are no more loops
3552  // break out of the loop when ((CumulativeLength - FrontElementLength) < DistanceAtHalfBraking ) && ((!BuffersOrContinuationNowFlag && !ContinuationNextFlag) || SignallerStoppingFlag);
3553 
3554  do
3555  {
3556  RedSignalFlag = false;
3557  BuffersFlag = false;
3558  StationFlag = false;
3559  BuffersOrContinuationNowFlag = false;
3560  ContinuationNextFlag = false;
3561  // have to reset this after the above test
3562  // add current element length to CumulativeLength
3563  CumulativeLength += (2 * CurrentElementHalfLength);
3564  if((CumulativeLength >= DistanceAtThreeQuarterBraking) && (TrainMode == Signaller) && SignallerStoppingFlag)
3565  {
3566  SignallerStopRequired = true;
3567  // once set stays set until SignallerStoppingFlag reset, providing !BuffersOrContinuationNowFlag,
3568  // set SignallerStopBrakeRate to stop in CumulativeLength unless already higher (i.e. can only increase)
3569  double TempBR = EntrySpeed * EntrySpeed / 2 / 3.6 / 3.6 / CumulativeLength;
3570  if(SignallerStopBrakeRate < TempBR)
3571  {
3572  SignallerStopBrakeRate = TempBR;
3573  }
3574  }
3575  // first check for stops within the length of the current element, where don't want any more checks & don't want
3576  // to add in any extra to the CumulativeLength. Only applies for buffers & station stops as signals should have been caught
3577  // during the last loop when the NextTrackVectorPosition was the signal.
3578 
3579  // check if current element is a buffer
3580  if(Track->TrackElementAt(374, CurrentTrackVectorPosition).TrackType == Buffers)
3581  {
3582  // no need to add in the length of this element to CumulativeLength as already included
3583  BuffersFlag = true;
3584  }
3585  // check if current element is a station stop
3586  if(TrainMode == Timetable)
3587  {
3588  bool StopRequired = false;
3589  if(!TimetableFinished && (NameInTimetableBeforeCDT(12, Track->TrackElementAt(375, CurrentTrackVectorPosition).ActiveTrackElementName,
3590  StopRequired) > -1) && ((Track->TrackElementAt(376, CurrentTrackVectorPosition).StationEntryStopLinkPos1 == EntryPos) ||
3591  (Track->TrackElementAt(377, CurrentTrackVectorPosition).StationEntryStopLinkPos2 == EntryPos)))
3592  {
3593  // no need to add in the length of element to CumulativeLength
3594  if(StopRequired)
3595  {
3596  StationFlag = true;
3597  }
3598  }
3599  }
3600  else
3601  {
3602  StationFlag = false;
3603  }
3604 
3605  // set NextHalfLength & NextSpeedLimit, but only if current element not buffers or exit continuation - no next element for them
3606  if(((Track->TrackElementAt(354, CurrentTrackVectorPosition).TrackType == Buffers) || (Track->TrackElementAt(355,
3607  CurrentTrackVectorPosition).TrackType == Continuation)) && (EntryPos == 1))
3608  {
3609  BuffersOrContinuationNowFlag = true;
3610  }
3611  if(!BuffersOrContinuationNowFlag && !BuffersFlag && !StationFlag) // skip if buffers or station flags already set
3612  {
3613  if(Track->TrackElementAt(356, CurrentTrackVectorPosition).TrackType == Points)
3614  {
3615  if((EntryPos == 0) || (EntryPos == 2))
3616  {
3617  if(Track->TrackElementAt(357, CurrentTrackVectorPosition).Attribute == 0)
3618  ExitPos = 1;
3619  else
3620  ExitPos = 3;
3621  }
3622  else
3623  ExitPos = 0;
3624  }
3625  else
3626  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
3627  NextTrackVectorPosition = Track->TrackElementAt(358, CurrentTrackVectorPosition).Conn[ExitPos];
3628  NextEntryPos = Track->TrackElementAt(359, CurrentTrackVectorPosition).ConnLinkPos[ExitPos];
3629  if(NextTrackVectorPosition > -1)
3630  {
3631  if(Track->TrackElementAt(845, NextTrackVectorPosition).TrackType == Points)
3632  // this test & section added at v0.6
3633  {
3634  if((NextEntryPos == 0) || (NextEntryPos == 2))
3635  {
3636  if(Track->TrackElementAt(846, NextTrackVectorPosition).Attribute == 0)
3637  {
3638  NextElementHalfLength = (Track->TrackElementAt(847, NextTrackVectorPosition).Length01) / 2;
3639  NextSpeedLimit = Track->TrackElementAt(848, NextTrackVectorPosition).SpeedLimit01;
3640  }
3641  else
3642  {
3643  NextElementHalfLength = (Track->TrackElementAt(849, NextTrackVectorPosition).Length23) / 2;
3644  NextSpeedLimit = Track->TrackElementAt(850, NextTrackVectorPosition).SpeedLimit23;
3645  }
3646  }
3647  else if(NextEntryPos == 1)
3648  {
3649  NextElementHalfLength = (Track->TrackElementAt(851, NextTrackVectorPosition).Length01) / 2;
3650  NextSpeedLimit = Track->TrackElementAt(852, NextTrackVectorPosition).SpeedLimit01;
3651  }
3652  else // == 3
3653  {
3654  NextElementHalfLength = (Track->TrackElementAt(853, NextTrackVectorPosition).Length23) / 2;
3655  NextSpeedLimit = Track->TrackElementAt(854, NextTrackVectorPosition).SpeedLimit23;
3656  }
3657  }
3658  else
3659  {
3660  if(NextEntryPos > 1)
3661  {
3662  NextElementHalfLength = (Track->TrackElementAt(360, NextTrackVectorPosition).Length23) / 2;
3663  NextSpeedLimit = Track->TrackElementAt(361, NextTrackVectorPosition).SpeedLimit23;
3664  }
3665  else
3666  {
3667  NextElementHalfLength = (Track->TrackElementAt(362, NextTrackVectorPosition).Length01) / 2;
3668  NextSpeedLimit = Track->TrackElementAt(363, NextTrackVectorPosition).SpeedLimit01;
3669  }
3670  }
3671  }
3672  else
3673  {
3674  throw Exception("Error - Trying to access NextTrackVectorPosition when none present in SetTrainMovementValues");
3675  }
3676  // now check for stops, first cover those where don't want to add in length of next element
3677  // check if next element is a red signal - Attr 0,
3678  // note that this doesn't apply to trains stopped at a red signal since the signal position is
3679  // CurrentTrackVectorPosition not NextTrackVectorPosition
3680  bool StopRequired;
3681  if(Track->TrackElementAt(364, NextTrackVectorPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal)
3682  {
3683  if(Track->TrackElementAt(365, NextTrackVectorPosition).Attribute == 0)
3684  // no need to add in the length of element to CumulativeLength
3685  RedSignalFlag = true;
3686  // next element is a red signal
3687  }
3688  // check if current element is a station & next element contains a train - trains will always stop without crashing at a
3689  // station they are due to stop at even if there is a train in front blocking the normal stop position - providing there is
3690  // at least one platform element free
3692  CurrentTrackVectorPosition).ActiveTrackElementName, StopRequired) > -1) && Track->OtherTrainOnTrack(3, NextTrackVectorPosition,
3693  NextEntryPos, TrainID))
3694  {
3695  // no need to add in the length of element to CumulativeLength
3696  if(StopRequired)
3697  StationFlag = true;
3698  }
3699  // check if next element contains a train & in Signaller mode (always stops for train in front if in signaller mode)
3700  else if((TrainMode == Signaller) && Track->OtherTrainOnTrack(4, NextTrackVectorPosition, NextEntryPos, TrainID))
3701  // (Track->TrackElementAt(651, NextTrackVectorPosition).TrainIDOnElement > -1))
3702  {
3703  // no need to add in the length of element to CumulativeLength
3704  TrainInFrontInSignallerModeFlag = true;
3705  }
3706  // check if next element is a buffer
3707  else if(Track->TrackElementAt(366, NextTrackVectorPosition).TrackType == Buffers)
3708  {
3709  // need to add in the length of that element to CumulativeLength
3710  CumulativeLength += Track->TrackElementAt(367, NextTrackVectorPosition).Length01;
3711  BuffersFlag = true;
3712  }
3713  // check if next element is a station stop
3715  NextTrackVectorPosition).ActiveTrackElementName, StopRequired) > -1) && ((Track->TrackElementAt(371,
3716  NextTrackVectorPosition).StationEntryStopLinkPos1 == EntryPos) || (Track->TrackElementAt(372,
3717  NextTrackVectorPosition).StationEntryStopLinkPos2 == EntryPos)))
3718  {
3719  // need to add in the length of that element to CumulativeLength if a stop required
3720  if(StopRequired)
3721  {
3722  StationFlag = true;
3723  CumulativeLength += Track->TrackElementAt(373, NextTrackVectorPosition).Length01;
3724  }
3725  }
3726  }
3727  //now can decide whether need to stop over CumulativeLength
3728  if(RedSignalFlag || BuffersFlag || StationFlag || TrainInFrontInSignallerModeFlag || SignallerStopRequired || StepForwardFlag) // no more loops
3729  {
3730  // have to come to a stop over CumulativeLength
3731  if(CumulativeLength == FrontElementLength)
3732  // will be if StepForwardFlag (if stopped to begin with on zero power then earlier check will intercept it and it won't reach here
3733  // only one length to go before stop so check whether need to accelerate for half length then brake for latter
3734  // half; calc speed at halfway point that corresponds to half braking rate for latter half of track element,
3735  // and if less than EntrySpeed then skip this section (don't need any acceleration)
3736  // if not calc speed at halfway point & if less than above set half speed to this value;
3737  // use constant acceleration in calculating half time point
3738  {
3739  MaxExitSpeed = 0;
3740  double MaxHalfSpeed;
3741  double MaxHalfSpeedAtHalfBraking = 3.6 * sqrt(MaxBrakeRate * FrontElementLength / 2);
3742  // have to halve the element length, & can't be zero or negative so no need to test
3743  // if(MaxHalfSpeedAtHalfBraking > LimitingSpeed) MaxHalfSpeed = LimitingSpeed; else MaxHalfSpeed = MaxHalfSpeedAtHalfBraking;
3744  if(MaxHalfSpeedAtHalfBraking > FrontElementMaxSpeed)
3745  MaxHalfSpeed = FrontElementMaxSpeed;
3746  else
3747  MaxHalfSpeed = MaxHalfSpeedAtHalfBraking;
3748  if(MaxHalfSpeed > (2 * EntrySpeed))
3749  // use 2x to prevent kangarooing at last element when had
3750  // been braking smoothly at less that 50% braking rate, 2x should prevent all but extreme cases
3751  {
3752  ExitSpeedHalf = 3.6 * Power(((1.5 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)),
3753  0.333334);
3754  bool HalfSpeedLimited = false;
3755  if(MaxHalfSpeed < ExitSpeedHalf)
3756  {
3757  ExitSpeedHalf = MaxHalfSpeed;
3758  HalfSpeedLimited = true;
3759  }
3760  if(PowerAtRail > 1)
3761  // added at v2.4.0 in case reach here with failed train, when can't use AValue in denominator as close zero
3762  { // [km/h/3.6 = m/s]
3763  ExitTimeHalf =
3764  EntryTime + TDateTime(((ExitSpeedHalf * ExitSpeedHalf) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue) / 86400.0);
3765  }
3766  else
3767  {
3768  ExitTimeHalf = EntryTime + TDateTime(EntryHalfLength * 3.6 / EntrySpeed / 86400.0);
3769  }
3770  // the above is the time taken to accelerate to ExitSpeedHalf, so if this is reached before the half
3771  // way point (i.e. HalfSpeedLimited is set) then the time will be too short; change later to equal the
3772  // braking time; not fully accurate but better to be equal than have a short acceleration period followed
3773  // by a long braking period
3774  ExitSpeedFull = 0;
3775  TempBrakeRate = ExitSpeedHalf * ExitSpeedHalf / 2 / 3.6 / 3.6 / EntryHalfLength;
3776  if(TempBrakeRate > MaxBrakeRate)
3777  TempBrakeRate = MaxBrakeRate;
3778  // shouldn't be but leave in anyway
3779  if(TempBrakeRate > BrakeRate)
3780  BrakeRate = TempBrakeRate;
3781  // BrakeRate may already have been set in an earlier loop so don't want to reduce it
3782  ExitTimeFull = ExitTimeHalf + TDateTime(ExitSpeedHalf / 3.6 / BrakeRate / 86400.0);
3783  if(HalfSpeedLimited)
3784  // this is the change referred to above
3785  {
3786  TDateTime BrakingTime = ExitTimeFull - ExitTimeHalf;
3787  ExitTimeHalf = EntryTime + BrakingTime;
3788  ExitTimeFull = ExitTimeHalf + BrakingTime;
3789  }
3790  OneLengthAccelDecel = true; //used in TrackTrainFloat in InterfaceUnit.cpp to show accelerating for first half move then decelerating
3791  Utilities->CallLogPop(1095);
3792  return;
3793  }
3794  }
3795  // set braking to achieve speed = 0 @ CumulativeLength up to MaxBrakeRate
3796  // calc MaxExitSpeed for element at EntryPosition & set to this or existing val if lower,
3797  // calc th, tf, sh, & sf
3798  TempBrakeRate = EntrySpeed * EntrySpeed / 2 / 3.6 / 3.6 / CumulativeLength;
3799  if(TempBrakeRate > MaxBrakeRate)
3800  TempBrakeRate = MaxBrakeRate;
3801  if(TempBrakeRate > BrakeRate)
3802  BrakeRate = TempBrakeRate;
3803  // BrakeRate may already have been set in an earlier loop so don't want to reduce it
3804  if(SignallerStopRequired)
3805  // set BrakeRate to max of its calculated value or SignallerStopBrakeRate
3806  {
3808  {
3810  // this prevents the brakerate from reducing for a signaller stop
3811  // regardless of other conditions that may change as progress round the loop
3812  }
3813  }
3815  // prevents BrakeRate dropping below SignallerStopBrakeRate once it's been set whether or not SignallerStopRequired set
3816  // SignallerStopRequired may not be set if a red signal found in a later calc, & brakerate may then drop
3817  {
3819  }
3820  int TempMaxExitSpeed;
3821  // calc current value & if less than MaxExitSpeed set that to this
3822  MaxExitSpeedAtHalfBrakingSquared = 3.6 * 3.6 * MaxBrakeRate * (CumulativeLength - FrontElementLength);
3823  if(MaxExitSpeedAtHalfBrakingSquared < 10)
3824  MaxExitSpeedAtHalfBraking = 0;
3825  else
3826  MaxExitSpeedAtHalfBraking = sqrt(MaxExitSpeedAtHalfBrakingSquared);
3827  // if(MaxExitSpeedAtHalfBraking > LimitingSpeed) MaxExitSpeed = LimitingSpeed; else MaxExitSpeed = MaxExitSpeedAtHalfBraking;
3828  // I think the above was dropped because it could cause MaxExitSpeed to increase (MaxExitSpeed is an external variable retained between loops)
3829  if(MaxExitSpeedAtHalfBraking > FrontElementMaxSpeed)
3830  TempMaxExitSpeed = FrontElementMaxSpeed;
3831  else
3832  TempMaxExitSpeed = MaxExitSpeedAtHalfBraking;
3833  if(TempMaxExitSpeed < MaxExitSpeed)
3834  MaxExitSpeed = TempMaxExitSpeed;
3835 
3836  // here have EntrySpeed & MaxExitSpeed (for the next element), BrakeRate (to bring speed to zero over
3837  // Cumulativelength, and Cumulativelength
3838 
3839  if((EntrySpeed > MaxExitSpeed) || SignallerStopRequired || (SignallerStopBrakeRate > 0.01)) // need to brake
3840  {
3841  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * EntryHalfLength);
3842  if(ExitSpeedHalfSquared < 10)
3843  ExitSpeedHalf = 0;
3844  else
3845  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
3846  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / 3.6 / BrakeRate / 86400.0);
3847  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 4 * BrakeRate * EntryHalfLength);
3848  if(ExitSpeedFullSquared < 10)
3849  ExitSpeedFull = 0;
3850  else
3851  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
3852  if((StationFlag) && (CumulativeLength == FrontElementLength))
3853  {
3854  ExitSpeedFull = 0;
3855  // force a stop for station (not for buffers or red signal)
3856  }
3857  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / 3.6 / BrakeRate / 86400.0);
3858  }
3859  // new condition at v2.4.0
3860  else if(PowerAtRail <= 1)
3861  // use EntrySpeed, CumulativeLength & BrakeRate to calculate the half and full exit times and speeds for next element
3862  // avoid using AValue in denominator or have excessively long times
3863  {
3864  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * BrakeRate * FrontElementLength);
3865  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
3866  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / (3.6 * BrakeRate * 86400.0));
3867 
3868  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * FrontElementLength);
3869  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
3870  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / (3.6 * BrakeRate * 86400.0));
3871  }
3872  else // e.g. moving towards a signal or station after a speed limit, so can accelerate unless no power
3873  // without the power need above condition or have hours of delay times, above added at v2.4.0
3874  {
3875  // for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
3876  // (1) V^2/(3.6^2) - U^2/(3.6^2) = A^2T; (2) V = 3.6 * ((1.5*S*A^2) + U^3/(3.6^3))^0.333334;
3877  // where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph, S = distance & T = time, note that km/h/3.6 = m/s
3878  // This is a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
3879  BrakeRate = 0;
3880  ExitSpeedHalf = 3.6 * Power(((1.5 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)),
3881  0.333334);
3882  ExitSpeedFull = 3.6 * Power(((3 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
3883  // above valid for ExitSpeedHalf & Full <= MaxExitSpeed
3885  // can accelerate continually over the half length
3886  {
3887  ExitTimeHalf = EntryTime + TDateTime(((ExitSpeedHalf * ExitSpeedHalf) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue)
3888  / 86400.0);
3890  // can accelerate continually over the full length
3891  // i.e both (ExitSpeedHalf <= MaxExitSpeed) & (ExitSpeedFull <= MaxExitSpeed)
3892  {
3893  ExitTimeFull =
3894  EntryTime + TDateTime(((ExitSpeedFull * ExitSpeedFull) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue) / 86400.0);
3895  }
3896  else // (ExitSpeedHalf <= MaxExitSpeed) but (ExitSpeedFull > MaxExitSpeed)
3897  // accelerate to MaxExitSpeed then reamin at this speed for rest of element
3898  {
3899  // added at v0.6 as a safeguard
3900  if(MaxExitSpeed < EntrySpeed)
3902  // to prevent DeltaExitTimeToMaxInSecs being negative
3903  if(MaxExitSpeed < 1)
3904  MaxExitSpeed = 1;
3905  // to prevent divide by zero error
3906  // below as was
3908  double DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
3909  double DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
3910  (1.5 * AValue * AValue);
3911  double RemainingDistance = double(FrontElementLength) - DistanceToMax;
3912  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
3913  ExitTimeFull = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
3914  }
3915  }
3916  else // ExitSpeedHalf > MaxExitSpeed, so ExitSpeedFull must also be > MaxExitSpeed
3917  // accelerate over first half to MaxExitSpeed then remain at this speed for rest of the first and
3918  // second halves of the element
3919  {
3920  // added at v0.6 as a safeguard
3921  if(MaxExitSpeed < EntrySpeed)
3923  // to prevent DeltaExitTimeToMaxInSecs being negative
3924  if(MaxExitSpeed < 1)
3925  MaxExitSpeed = 1; // to prevent divide by zero error
3926  // below as was
3928  double DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
3929  double DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
3930  (1.5 * AValue * AValue);
3931  double RemainingDistance = double(FrontElementLength / 2) - DistanceToMax;
3932  // remaining distance to half length
3933  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
3934  ExitTimeHalf = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
3936  ExitTimeFull = ExitTimeHalf + TDateTime(3.6 * EntryHalfLength / MaxExitSpeed / 86400.0);
3937  }
3938  }
3939  Utilities->CallLogPop(706);
3940  return;
3941  }
3942  else
3943  {
3944  if(!BuffersOrContinuationNowFlag)
3945  {
3946  if(NextSpeedLimit < LimitingSpeed)
3947  LimitingSpeed = NextSpeedLimit;
3948  }
3949  // calc max exit speed at half braking to ensure don't accelerate past it (if acceleration is required)
3950  int TempMaxExitSpeed;
3951  // calc current value & if less than MaxExitSpeed set that to this
3952  // Note that LimitingSpeed is the max value at the end of CumulativeLength, so MaxExitSpeedAtHalfBrakingSquared will be larger
3953  MaxExitSpeedAtHalfBrakingSquared = (LimitingSpeed * LimitingSpeed) + (3.6 * 3.6 * MaxBrakeRate * (CumulativeLength - FrontElementLength));
3954  if(MaxExitSpeedAtHalfBrakingSquared < 10)
3955  MaxExitSpeedAtHalfBraking = 0;
3956  else
3957  MaxExitSpeedAtHalfBraking = sqrt(MaxExitSpeedAtHalfBrakingSquared);
3958  if(MaxExitSpeedAtHalfBraking > FrontElementMaxSpeed)
3959  TempMaxExitSpeed = FrontElementMaxSpeed;
3960  else
3961  TempMaxExitSpeed = MaxExitSpeedAtHalfBraking;
3962  if(TempMaxExitSpeed < MaxExitSpeed)
3963  MaxExitSpeed = TempMaxExitSpeed;
3964  // MaxExitSpeed is an external variable & this can reduce its value
3965  if(EntrySpeed > LimitingSpeed)
3966  // note that LimitingSpeed is more restrictive than MaxExitSpeed
3967  // calc TempBrakeRate & set BrakeRate to this or keep existing val if higher
3968  {
3969  TempBrakeRate = ((EntrySpeed * EntrySpeed) - (LimitingSpeed * LimitingSpeed)) / 3.6 / 3.6 / 2 / CumulativeLength;
3970  if(TempBrakeRate > MaxBrakeRate)
3971  TempBrakeRate = MaxBrakeRate;
3972  // shouldn't be for speedlimits since all known in advance, but include anyway
3973  if(TempBrakeRate > BrakeRate)
3974  BrakeRate = TempBrakeRate;
3975  // BrakeRate may already have been set in an earlier loop so don't want to reduce it
3976  }
3977  }
3978  if(!BuffersOrContinuationNowFlag)
3979  {
3980  CurrentTrackVectorPosition = NextTrackVectorPosition;
3981  EntryPos = NextEntryPos;
3982  CurrentElementHalfLength = NextElementHalfLength;
3983  if(Track->TrackElementAt(378, NextTrackVectorPosition).TrackType == Continuation)
3984  {
3985  ContinuationNextFlag = true;
3986  }
3987  }
3988  }
3989  while(((CumulativeLength - FrontElementLength) < DistanceAtHalfBraking) && ((!BuffersOrContinuationNowFlag && !ContinuationNextFlag) ||
3991  // check from the end of the front element, if include the front element and could brake during it, then will skip further loops
3992  // & miss a stop requirement just beyond the front element. happened in Richard Standing's railway where a new service introduced
3993  // on a 100m length, with 20m length after & then a red signal - train accelerated over the 100m then caused a SPAD as too short a
3994  // stopping distance after it.
3995 
3996  //(!BuffersOrContinuationNowFlag && !ContinuationNextFlag) true when no continuation on either the next element and next but one element.
3997  //There won't be a buffer on the next element or would have caught earlier, just using this flag for convenience.
3998 
3999  // If SignallerStoppingFlag then don't exit loop because of an imminent continuation, because continuation
4000  // not immediately in front (if it is then LeadElement will be the continuation & SignallerStoppingFlag will be reset in UpdateTrain()),
4001  // need to at least give a chance to stop on signaller command, if keep moving until continuation is immediately in front then will
4002  // exit loop & that is OK as don't want to stop so close to a continuation, if that happens it means that the command to stop was given
4003  // too late
4004 
4005  // set final braking or acc'n speed & time values
4006  if(BrakeRate > 0.01)
4007  {
4008  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * EntryHalfLength);
4009  if(ExitSpeedHalfSquared < 10)
4010  ExitSpeedHalf = 0;
4011  else
4012  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4013  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / 3.6 / BrakeRate / 86400.0);
4014  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 4 * BrakeRate * EntryHalfLength);
4015  if(ExitSpeedFullSquared < 10)
4016  ExitSpeedFull = 0;
4017  else
4018  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4019  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / 3.6 / BrakeRate / 86400.0);
4020  }
4021  else
4022  {
4023  // for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
4024  // (1) V^2/(3.6^2) - U^2/(3.6^2) = A^2T; (2) V = 3.6 * ((1.5*S*A^2) + U^3/ (3.6)^3)^0.333334;
4025  // where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph , S = distance in m & T = time
4026  // This is a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
4027 
4028  BrakeRate = 0;
4029  ExitSpeedHalf = 3.6 * Power(((1.5 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
4030  ExitSpeedFull = 3.6 * Power(((3 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
4031  // above valid for ExitSpeedHalf & Full <= MaxExitSpeed
4033  {
4034  if(PowerAtRail > 1)
4035  // added at v2.4.0 in case reach here with failed train, when can't use AValue in denominator as close zero
4036  { // [km/h/3.6 = m/s]
4037  ExitTimeHalf = EntryTime + TDateTime(((ExitSpeedHalf * ExitSpeedHalf) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue)
4038  / 86400.0);
4039  }
4040  else
4041  {
4042  ExitTimeHalf = EntryTime + TDateTime(EntryHalfLength * 3.6 / EntrySpeed / 86400.0);
4043  }
4044 
4046  // (ExitSpeedHalf <= MaxExitSpeed) & (ExitSpeedFull <= MaxExitSpeed)
4047  {
4048  if(PowerAtRail > 1)
4049  // added at v2.4.0 in case reach here with failed train, when can't use AValue in denominator as close zero
4050  { // [km/h/3.6 = m/s]
4051  ExitTimeFull = EntryTime + TDateTime(((ExitSpeedFull * ExitSpeedFull) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue)
4052  / 86400.0);
4053  }
4054  else
4055  {
4056  ExitTimeFull = EntryTime + TDateTime(2 * EntryHalfLength * 3.6 / EntrySpeed / 86400.0);
4057  }
4058  }
4059  else // (ExitSpeedHalf <= MaxExitSpeed) & (ExitSpeedFull > MaxExitSpeed)
4060  {
4061  // added at v0.6 as a safeguard
4062  if(MaxExitSpeed < EntrySpeed)
4064  // to prevent DeltaExitTimeToMaxInSecs being negative
4065  if(MaxExitSpeed < 1)
4066  MaxExitSpeed = 1; // to prevent divide by zero error
4067  // below as was
4069  double DeltaExitTimeToMaxInSecs;
4070  double DistanceToMax;
4071  if(PowerAtRail > 1) // added at v2.4.0
4072  {
4073  DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4074  DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4075  (1.5 * AValue * AValue);
4076  }
4077  else // shouldn't ever get here given that ExitSpeedFull > ExitSpeedHalf
4078  {
4079  DeltaExitTimeToMaxInSecs = 2 * EntryHalfLength * 3.6 / EntrySpeed;
4080  // these not really accurate but will be good enough
4081  DistanceToMax = EntryHalfLength;
4082  }
4083  double RemainingDistance = double(FrontElementLength) - DistanceToMax;
4084  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4085  ExitTimeFull = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4086  }
4087  }
4088  else // ExitSpeedHalf > MaxExitSpeed, ExitSpeedFull must be > MaxExitSpeed
4089  {
4090  // added at v0.6 as a safeguard
4091  if(MaxExitSpeed < EntrySpeed)
4093  // to prevent DeltaExitTimeToMaxInSecs being negative
4094  if(MaxExitSpeed < 1)
4095  MaxExitSpeed = 1; // to prevent divide by zero error
4096  // below as was
4098  double DeltaExitTimeToMaxInSecs;
4099  double DistanceToMax;
4100  if(PowerAtRail > 1) // added at v2.4.0
4101  {
4102  DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4103  DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4104  (1.5 * AValue * AValue);
4105  }
4106  else
4107  {
4108  DeltaExitTimeToMaxInSecs = 2 * EntryHalfLength * 3.6 / EntrySpeed;
4109  // these not really accurate but will be good enough
4110  DistanceToMax = EntryHalfLength / 2;
4111  }
4112  double RemainingDistance = double(FrontElementLength / 2) - DistanceToMax; // remaining distance to half length
4113  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4114  ExitTimeHalf = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4116  ExitTimeFull = ExitTimeHalf + TDateTime(3.6 * EntryHalfLength / MaxExitSpeed / 86400.0);
4117  }
4118  }
4119  }
4120 
4121  else // SPADFlag set
4122  {
4124  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * EntryHalfLength);
4125  if(ExitSpeedHalfSquared < 10)
4126  ExitSpeedHalf = 0;
4127  else
4128  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4129  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / 3.6 / BrakeRate / 86400.0);
4130  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 4 * BrakeRate * EntryHalfLength);
4131  if(ExitSpeedFullSquared < 10)
4132  ExitSpeedFull = 0;
4133  else
4134  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4135  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / 3.6 / BrakeRate / 86400.0);
4136 
4137  // check if the exit speed is < 80% of the stopping speed for the next element, and if so stop at end of this element
4138  // this is because would stop short of end of next element (in reality the time to reach the end of the next element
4139  // would be too short (could be so short as to make the train jump) as time is calculated purely on speed & brake rate);
4140  // 80% is used as the brake rate might be set to come to a halt at the end of the next element in which case the speed
4141  // will be the stopping speed.
4142  if(ExitSpeedFull > 0)
4143  {
4144  if(Track->TrackElementAt(746, CurrentTrackVectorPosition).TrackType == Points)
4145  {
4146  if((EntryPos == 0) || (EntryPos == 2))
4147  {
4148  if(Track->TrackElementAt(747, CurrentTrackVectorPosition).Attribute == 0)
4149  ExitPos = 1;
4150  else
4151  ExitPos = 3;
4152  }
4153  else
4154  ExitPos = 0;
4155  }
4156  else
4157  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4158  NextTrackVectorPosition = Track->TrackElementAt(748, CurrentTrackVectorPosition).Conn[ExitPos];
4159  NextEntryPos = Track->TrackElementAt(749, CurrentTrackVectorPosition).ConnLinkPos[ExitPos];
4160  if(NextTrackVectorPosition > -1) // not a continuation or buffer
4161  {
4162  int NextElementLength;
4163  if(NextEntryPos > 1)
4164  {
4165  NextElementLength = (Track->TrackElementAt(750, NextTrackVectorPosition).Length23);
4166  }
4167  else
4168  {
4169  NextElementLength = (Track->TrackElementAt(751, NextTrackVectorPosition).Length01);
4170  }
4171  double NextStoppingSpeed = sqrt(3.6 * 3.6 * 2 * BrakeRate * NextElementLength);
4172  if(ExitSpeedFull < (0.8 * NextStoppingSpeed))
4173  {
4174  ExitSpeedFull = 0;
4175  }
4176  }
4177  }
4178  }
4179  // allow all values to be set normally in case need to brake, then test for zero power & need to coast
4180  if(PowerAtRail < 1) // new at v2.4.0 note that km/h/3.6 = m/s
4181  { // bring to a stop in 20 elements at 100km/h & assume each 100m long for calculating exit times but if on a continuation maintain speed
4182  if(LeadElement > -1)
4183  {
4185  // don't stop on a continuation either entering or leaving
4186  {
4189  MaxExitSpeed = LimitingSpeed;
4190  BrakeRate = 0;
4191  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed) / 86400.0));
4192  // assume length is 50m for ease of calc
4193  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf) / 86400.0));
4194  Utilities->CallLogPop(2126);
4195  return;
4196  }
4197  }
4198  else if(MidElement > -1)
4199  {
4201  {
4204  MaxExitSpeed = LimitingSpeed;
4205  BrakeRate = 0;
4206  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed) / 86400.0));
4207  // assume length is 50m for ease of calc
4208  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf) / 86400.0));
4209  Utilities->CallLogPop(2127);
4210  return;
4211  }
4212  }
4213  else if(LagElement > -1)
4214  {
4216  {
4219  MaxExitSpeed = LimitingSpeed;
4220  BrakeRate = 0;
4221  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed) / 86400.0));
4222  // assume length is 50m for ease of calc
4223  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf) / 86400.0));
4224  Utilities->CallLogPop(2128);
4225  return;
4226  }
4227  }
4228 
4229  if(EntrySpeed > 7.5) // keep going for at least another element
4230  {
4231  ExitSpeedHalf = EntrySpeed - 2.5;
4232  ExitSpeedFull = EntrySpeed - 5;
4233  MaxExitSpeed = LimitingSpeed;
4234  BrakeRate = 0;
4235  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed - 1.25) / 86400.0));
4236  // assume length is 50m for ease of calc
4237  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf - 1.25) / 86400.0));
4238  Utilities->CallLogPop(2129);
4239  return;
4240  }
4241  else // stop immediately, don't enter next element, floating label had displayed last exit speed full on 2nd half move so with zero when fully on element
4242  { // will appear to have slowed at steady rate
4243  ExitSpeedHalf = 0;
4244  ExitSpeedFull = 0;
4245  MaxExitSpeed = LimitingSpeed;
4246  BrakeRate = 0;
4247  ExitTimeHalf = EntryTime + TDateTime(1/24); //set this high in case used later though unlikely
4248  ExitTimeFull = EntryTime + TDateTime(1/23); //set about 2.5 mins later than half time
4249  StoppedWithoutPower = true;
4250  Utilities->CallLogPop(2130);
4251  return;
4252  }
4253  }
4254 
4255  // TempBrakeRate=MinSingle; TempBrakeRate=MaxSingle; TempBrakeRate=MinDouble; TempBrakeRate=MaxDouble;//included to stop warnings from unused declarations in math.hpp
4256  // TempBrakeRate=MinExtended; TempBrakeRate=MaxExtended; TempBrakeRate=MinComp; TempBrakeRate=MaxComp;//included to stop warnings from unused declarations in math.hpp
4257  Utilities->CallLogPop(707);
4258 }
4259 // ---------------------------------------------------------------------------
4260 /*
4261  bool TTrain::IsTerminalStation(int TrackVectorPosition, int EntryPos)
4262  {
4263  int NextExitPos;
4264  TTrackElement NextElement = Track->TrackElementAt(379, TrackVectorPosition), TempElement;
4265  if(TimetableVector.empty()) return false;
4266  if(NextElement.ActiveTrackElementName != TimetableVector.begin()->LocationName) return false;
4267  while((NextElement.ActiveTrackElementName == TimetableVector.begin()->LocationName) && (NextElement.TrackType != Buffers))
4268  {
4269  //check for points & follow attribute, but don't worry about a derail as that dealt with elsewhere
4270  if((NextElement.TrackType != Points) || ((EntryPos != 0) && (EntryPos != 2)))
4271  {
4272  NextExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4273  }
4274  else if((NextElement.TrackType == Points) && ((EntryPos == 0) || (EntryPos == 2)))
4275  {
4276  if(NextElement.Attribute == 0) NextExitPos = 1; else NextExitPos = 3;
4277  }
4278  TempElement = Track->TrackElementAt(380, NextElement.Conn[NextExitPos]);//need temp as NextElement used in next step
4279  NextElement = TempElement;
4280  }
4281  if(NextElement.ActiveTrackElementName != TimetableVector.begin()->LocationName) return false;
4282  if(NextElement.TrackType == Buffers) return true;
4283  return false;//shouldn't reach here but include to prevent compiler return warning
4284  }
4285 */
4286 // ---------------------------------------------------------------------------
4287 
4288 int TTrain::NameInTimetableBeforeCDT(int Caller, AnsiString Name, bool &Stop)
4289 /*
4290  returns the number by which the train ActionVectorEntryPtr needs
4291  to be incremented to point to the location arrival entry or passtime entry before a change of direction. Used to display missed
4292  actions when a stop or pass location has been reached before other timetabled actions have been carried out. If can't find it, or Name
4293  is "", -1 is returned. A change of direction is the limit of the search because a train may not stop at a location on the way out
4294  but stop on way back, and in these circumstances no actions have been missed. Stop indicates whether the train will stop at (true)
4295  or pass (false) the location.
4296 */ {
4297  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",NameInTimetableBeforeCDT," + Name + "," + HeadCode);
4298  Stop = false;
4299  if(TimetableFinished || (Name == ""))
4300  {
4301  Utilities->CallLogPop(957);
4302  return -1;
4303  }
4304  // start looking from current pointer position
4305  for(TActionVectorEntry * Ptr = ActionVectorEntryPtr; Ptr < &TrainDataEntryPtr->ActionVector.back(); Ptr++)
4306  {
4307  if((Ptr->Command == "cdt") || (Ptr->FormatType == Repeat))
4308  {
4309  break;
4310  }
4311  if((Ptr->ArrivalTime > TDateTime(-1)) && (Ptr->LocationName == Name))
4312  {
4313  if((Ptr->FormatType == TimeLoc) || (Ptr->FormatType == TimeTimeLoc))
4314  {
4315  Stop = true;
4316  Utilities->CallLogPop(960);
4317  return (Ptr - ActionVectorEntryPtr);
4318  }
4319  }
4320  if((Ptr->EventTime > TDateTime(-1)) && (Ptr->LocationName == Name) && (Ptr->Command == "pas"))
4321  {
4322  Utilities->CallLogPop(1517);
4323  return (Ptr - ActionVectorEntryPtr);
4324  }
4325  }
4326  Utilities->CallLogPop(959);
4327  return -1; // not found a valid entry
4328 }
4329 
4330 // ---------------------------------------------------------------------------
4331 
4333 /* Checks forward from train LeadElement, following leading point attributes but ignoring trailing point attributes,
4334  until finds either a train or a signal/buffers/continuation/loop. If finds a train returns false, else returns true.
4335  Ignores the call-on signal.
4336 */ {
4337  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ClearToNextSignal" + "," + HeadCode);
4338  int ReturnVal = 0;
4339 
4340  for(unsigned int x = 0; x < Track->TrackVector.size(); x++)
4341  {
4342  Track->TrackVector.at(x).TempTrackMarker01 = false;
4343  Track->TrackVector.at(x).TempTrackMarker23 = false;
4344  }
4345  int CurrentTrackVectorPosition = LeadElement, NextTrackVectorPosition;
4346  int EntryPos = LeadEntryPos, ExitPos, NextEntryPos;
4347 
4348  while(true)
4349  {
4350  if((Track->TrackElementAt(382, CurrentTrackVectorPosition).TrainIDOnElement > -1) && (Track->TrackElementAt(383,
4351  CurrentTrackVectorPosition).TrainIDOnElement != TrainID))
4352  {
4353  ReturnVal = 1;
4354  break;
4355  }
4356  if(((Track->TrackElementAt(384, CurrentTrackVectorPosition).TrackType == Buffers) || (Track->TrackElementAt(385,
4357  CurrentTrackVectorPosition).TrackType == Continuation)) && (EntryPos == 1))
4358  {
4359  ReturnVal = 2;
4360  break;
4361  }
4362  if((EntryPos < 2) && (Track->TrackElementAt(386, CurrentTrackVectorPosition).Config[1 - EntryPos] == Signal) && (Track->TrackElementAt(529,
4363  CurrentTrackVectorPosition).Attribute != 4)) // Attr 4 == call-on signal
4364  {
4365  ReturnVal = 3;
4366  break;
4367  }
4368  if((Track->TrackElementAt(387, CurrentTrackVectorPosition).TrackType == Bridge) || (Track->TrackElementAt(388,
4369  CurrentTrackVectorPosition).TrackType == Crossover))
4370  {
4371  if((EntryPos < 2) && (Track->TrackElementAt(523, CurrentTrackVectorPosition).TempTrackMarker01))
4372  // must be a loop - reached same point as examined earlier
4373  {
4374  ReturnVal = 4;
4375  break;
4376  }
4377  else if((EntryPos > 1) && (Track->TrackElementAt(524, CurrentTrackVectorPosition).TempTrackMarker23))
4378  {
4379  ReturnVal = 4;
4380  break;
4381  }
4382  }
4383  else
4384  {
4385  if((Track->TrackElementAt(525, CurrentTrackVectorPosition).TempTrackMarker01) || (Track->TrackElementAt(526,
4386  CurrentTrackVectorPosition).TempTrackMarker23))
4387  {
4388  ReturnVal = 4;
4389  break;
4390  }
4391  }
4392  if(EntryPos < 2)
4393  Track->TrackElementAt(389, CurrentTrackVectorPosition).TempTrackMarker01 = true;
4394  else
4395  Track->TrackElementAt(527, CurrentTrackVectorPosition).TempTrackMarker23 = true;
4396 
4397  if(Track->TrackElementAt(390, CurrentTrackVectorPosition).TrackType == Points)
4398  {
4399  if((EntryPos == 0) || (EntryPos == 2))
4400  {
4401  if(Track->TrackElementAt(391, CurrentTrackVectorPosition).Attribute == 0)
4402  ExitPos = 1;
4403  else
4404  ExitPos = 3;
4405  }
4406  else
4407  ExitPos = 0;
4408  }
4409  else
4410  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4411  NextTrackVectorPosition = Track->TrackElementAt(392, CurrentTrackVectorPosition).Conn[ExitPos];
4412  NextEntryPos = Track->TrackElementAt(393, CurrentTrackVectorPosition).ConnLinkPos[ExitPos];
4413  CurrentTrackVectorPosition = NextTrackVectorPosition;
4414  EntryPos = NextEntryPos;
4415  }
4416  if(ReturnVal == 1)
4417  {
4418  Utilities->CallLogPop(708);
4419  return false;
4420  }
4421  if(ReturnVal == 2)
4422  {
4423  Utilities->CallLogPop(709);
4424  return true;
4425  }
4426  if(ReturnVal == 3)
4427  {
4428  Utilities->CallLogPop(946);
4429  return true;
4430  }
4431  if(ReturnVal == 4)
4432  {
4433  Utilities->CallLogPop(947);
4434  return true;
4435  }
4436  else
4437  {
4438  throw Exception("Error - failed to set ReturnVal in ClearToNextSignal()");
4439  }
4440 }
4441 
4442 // ---------------------------------------------------------------------------
4443 
4445 /*
4446  Check whether calling-on conditions met - a) approaching train has stopped at a signal but not at a location;
4447  b) if there is a facing train at the station, it is being held appropriately (must be awaiting a join (Fjo or jbo) or a
4448  change of direction (cdt), remaining here (Frh), or under signaller control);
4449  c) at least 1 platform available for the approaching train; d) points (if any) set for direct route into platform;
4450  e) approaching train is to stop at station; f) no more facing signals between train and platform; g) [dropped g]
4451  h) train in front preventing route being set far enough to release stop signal; i) train in front not exiting at continuation; j) signal must be within 4km of
4452  the stop platform; k) [dropped (k), now can set a reoute or part route into platform so can set points more easily.] and l) no existing route conflicts with the route into the platform
4453  If all OK & route or part route not already set then set an unrestricted route into the station (just to the first platform), to prevent point changing or other route conflicts - if a partial route set than can still
4454  change points outside the route or have a route conflict if another route is set.
4455 */ {
4456  if(Track->RouteFlashFlag)
4457  return false;
4458 
4459  // don't want to create a new route from the stop signal if one is already in construction as
4460  // some of the callingon route elements may be involved
4461  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CallingOnAllowed" + "," + HeadCode);
4462  bool PlatformFoundFlag = false, StopRequired = false, SkipRouteCheck = false, RouteOrPartRouteSet = false; // last added at v1.2.0
4463  int CurrentTrackVectorPosition = LeadElement, NextTrackVectorPosition, ElementNumber = 0, Distance = 0;
4464  int RouteStartPosition;
4465  // this is the track vector position of the start element for the new unrestricted route - one past the stop signal
4466  int PlatformPosition;
4467  // the track vector position of the first stop platfrom
4468  int EntryPos = LeadEntryPos, ExitPos, NextEntryPos, RouteID;
4469  // not used here
4470  AnsiString LeadStationName = Track->TrackElementAt(395, LeadElement).ActiveTrackElementName; // still OK even if ""
4471 
4472  // must be stopped at a signal but not at a location & still in timetable (a)
4473 
4475  // no need to check for SignallerStopped as this function only called in Timetable mode
4476  {
4477  Utilities->CallLogPop(711);
4478  return false;
4479  }
4480  while(true)
4481  {
4482  TTrackElement &CurrentTrackElement = Track->TrackElementAt(396, CurrentTrackVectorPosition);
4483  // don't look further than 4km ahead (j)
4484  if(Distance > 4000)
4485  {
4486  Utilities->CallLogPop(967);
4487  return false;
4488  }
4489  // if find another train on an element in front, before find a valid platform, return false (c)
4490  if((CurrentTrackElement.TrainIDOnElement > -1) && (CurrentTrackElement.TrainIDOnElement != TrainID) && !PlatformFoundFlag)
4491  {
4492  Utilities->CallLogPop(713);
4493  return false;
4494  }
4495  // if find another train in front when there is a valid platform (keep searching after find a platform as train may still
4496  // be facing later on)
4497  if((CurrentTrackElement.TrainIDOnElement > -1) && (CurrentTrackElement.TrainIDOnElement != TrainID) && PlatformFoundFlag)
4498  {
4499  // get LeadElement, if -1 return (could be exiting at continuation) (i)
4500  TTrain OtherTrain = TrainController->TrainVectorAtIdent(12, CurrentTrackElement.TrainIDOnElement);
4501  if(OtherTrain.LeadElement == -1)
4502  {
4503  Utilities->CallLogPop(714);
4504  return false;
4505  }
4506  // if a facing train then make sure it is awaiting a join (Fjo or jbo) or a change of direction (cdt), or remaining here (Frh) (b)
4507  if(OtherTrain.LeadElement == CurrentTrackVectorPosition)
4508  {
4509  AnsiString OtherCommand = OtherTrain.ActionVectorEntryPtr->Command;
4510  if((OtherCommand == "Fjo") || (OtherCommand == "jbo") || (OtherCommand == "cdt") || (OtherCommand == "Frh") ||
4511  (OtherTrain.TrainMode == Signaller))
4512  {
4513  break;
4514  }
4515  else
4516  {
4517  Utilities->CallLogPop(955);
4518  return false;
4519  }
4520  }
4521  else // (h)
4522  {
4523  break;
4524  }
4525  }
4526  // if reach buffers or exit continuation return false (can set route)
4527  if(((CurrentTrackElement.TrackType == Buffers) || (CurrentTrackElement.TrackType == Continuation)) && (EntryPos == 1))
4528  {
4529  Utilities->CallLogPop(716);
4530  return false;
4531  }
4532  // if reach forward signal (other than the one the train is waiting at) return false (can set route) (f)
4533  if((EntryPos < 2) && (CurrentTrackElement.Config[1 - EntryPos] == Signal) && (CurrentTrackVectorPosition != Track->TrackElementAt(404,
4535  {
4536  Utilities->CallLogPop(717);
4537  return false;
4538  }
4539  // if reach a location that isn't in timetable return false - drop this as still can't set a route
4540 /*
4541  if((Track->TrackElementAt(405, CurrentTrackVectorPosition).ActiveTrackElementName != "") && (Track->TrackElementAt(406, CurrentTrackVectorPosition).ActiveTrackElementName != LeadStationName) &&
4542  (NameInTimetableBeforeCDT(14, Track->TrackElementAt(407, CurrentTrackVectorPosition).ActiveTrackElementName) == -1))
4543  {
4544  Utilities->CallLogPop(718);
4545  return false;
4546  }
4547 */
4548  // if reach a location that is in timetable set PlatformFoundFlag (but not if position is points set to diverge) (e)
4549  if((CurrentTrackElement.ActiveTrackElementName != "") && (CurrentTrackElement.ActiveTrackElementName != LeadStationName) &&
4550  (NameInTimetableBeforeCDT(15, CurrentTrackElement.ActiveTrackElementName, StopRequired) > -1))
4551  {
4552  if(StopRequired)
4553  {
4554  if((CurrentTrackElement.TrackType != Points) || ((CurrentTrackElement.TrackType == Points) && (CurrentTrackElement.Attribute == 0)))
4555  {
4556  if(!PlatformFoundFlag)
4557  PlatformPosition = CurrentTrackVectorPosition;
4558  // ensure this only set once at first valid platform position - the unrestricted route will end here
4559  PlatformFoundFlag = true;
4560  }
4561  }
4562  }
4563  // Drop this below - was to prevent call-on if front train had left the station. Criterion now is not that front
4564  // train has to be at station but that has to be before the next forward signal
4565 /*
4566  if((Track->TrackElementAt(411, CurrentTrackVectorPosition).ActiveTrackElementName == "") && (PlatformFoundFlag))
4567  {
4568  Utilities->CallLogPop(719);
4569  return false;
4570  }
4571 */
4572  // make sure points are followed correctly (d) & set ExitPos
4573  if(CurrentTrackElement.TrackType == Points)
4574  {
4575  if((EntryPos == 0) || (EntryPos == 2))
4576  {
4577  if(CurrentTrackElement.Attribute == 0)
4578  ExitPos = 1;
4579  else
4580  ExitPos = 3;
4581  }
4582  if(EntryPos == 1)
4583  {
4584  if(CurrentTrackElement.Attribute == 0)
4585  ExitPos = 0;
4586  else
4587  {
4588  Utilities->CallLogPop(720);
4589  return false;
4590  }
4591  }
4592  if(EntryPos == 3)
4593  {
4594  if(CurrentTrackElement.Attribute == 1)
4595  ExitPos = 0;
4596  else
4597  {
4598  Utilities->CallLogPop(721);
4599  return false;
4600  }
4601  }
4602  }
4603  else
4604  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4605 
4606  // check existing routes - if element forward of the signal (ElementNumber == 2) is AutoSignals then OK without further checks as this route must extend to
4607  // the next signal so must at least reach the station, also if have another route set (must be unrestricted) from either the stop signal or the element after it
4608  // to or towards the platform (& all points set correctly) then OK, otherwise reject if (1) there are any route elements already set from element
4609  // forward of element after the signal to & including the first platform element (covers crossover with other route set) or (2) a fouled diagonal (k)
4610  if(ElementNumber < 2)
4611  SkipRouteCheck = true;
4612  else
4613  SkipRouteCheck = false;
4614  if(ElementNumber == 1) // the stop signal
4615  {
4616  RouteStartPosition = CurrentTrackVectorPosition;
4617  }
4618 /*
4619  if(ElementNumber == 2)
4620  {
4621  if(AllRoutes->GetRouteTypeAndNumber(18, CurrentTrackVectorPosition, EntryPos, RouteID) == AllRoutes->AutoSigsRoute) AutoSigs = true;
4622  else AutoSigs = false;
4623  if(AllRoutes->GetRouteTypeAndNumber(25, CurrentTrackVectorPosition, EntryPos, RouteID) == AllRoutes->NotAutoSigsRoute) OtherFullRouteSet = true;
4624  }
4625 */
4626  if(ElementNumber > 1)
4627  {
4628  if(AllRoutes->GetRouteTypeAndNumber(26, CurrentTrackVectorPosition, EntryPos, RouteID) != AllRoutes->NoRoute)
4629  RouteOrPartRouteSet = true;
4630  else
4631  RouteOrPartRouteSet = false;
4632  }
4633  if(!SkipRouteCheck && !RouteOrPartRouteSet)
4634  {
4635  if(AllRoutes->TrackIsInARoute(16, CurrentTrackVectorPosition, EntryPos)) // must be a conflicting route
4636  {
4637  Utilities->CallLogPop(1859);
4638  return false;
4639  }
4640  int ExitLink = CurrentTrackElement.Link[ExitPos];
4641  if((ExitLink == 1) || (ExitLink == 3) || (ExitLink == 7) || (ExitLink == 9))
4642  {
4643  if(AllRoutes->DiagonalFouledByRouteOrTrain(6, CurrentTrackElement.HLoc, CurrentTrackElement.VLoc, ExitLink))
4644  {
4645  Utilities->CallLogPop(1850);
4646  return false;
4647  }
4648  }
4649  }
4650 
4651  // finished all checks, now update CurrentTrackVectorPosition & EntryPos for the next iteration
4652  if(EntryPos < 2)
4653  Distance += CurrentTrackElement.Length01;
4654  else
4655  Distance += CurrentTrackElement.Length23;
4656  NextTrackVectorPosition = CurrentTrackElement.Conn[ExitPos];
4657  NextEntryPos = CurrentTrackElement.ConnLinkPos[ExitPos];
4658  CurrentTrackVectorPosition = NextTrackVectorPosition;
4659  EntryPos = NextEntryPos;
4660  ElementNumber++;
4661  } // while(true)
4662 
4663  // if all OK & autosigs route not already set then set an unrestricted route into the station (just to the first platform)
4664  // from the stop signal (note that it may be last in an autosigs route)
4665  // a single element route at the stop signal should have been removed prior to this function being called (that called before
4666  // this in ClockTimer2)
4667 
4668  // now add elements to the CallonVector
4669  TAllRoutes::TCallonEntry CallonEntry(RouteOrPartRouteSet, RouteStartPosition, PlatformPosition);
4670 
4671  AllRoutes->CallonVector.push_back(CallonEntry);
4672  Utilities->CallLogPop(1860);
4673  return true; // return false if fail to set route for any reason
4674 }
4675 
4676 // ---------------------------------------------------------------------------
4677 /*
4678  bool TTrain::TimetableFinished()
4679  {
4680  if((ActionVectorEntryPtr == TrainDataEntryPtr->ActionVector.end()) || (ActionVectorEntryPtr->FormatType == Repeat))//past all actions
4681  {
4682  return true;
4683  }
4684  return false;
4685  }
4686 */
4687 // ---------------------------------------------------------------------------
4688 
4689 AnsiString TTrain::GetTrainHeadCode(int Caller)
4690 
4691 {
4692  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetTrainHeadCode" + "," + HeadCode);
4693  AnsiString RepeatHeadCode = TrainController->GetRepeatHeadCode(0, HeadCode, RepeatNumber, IncrementalDigits);
4694 
4695  Utilities->CallLogPop(1452);
4696  return RepeatHeadCode;
4697 }
4698 
4699 // ---------------------------------------------------------------------------
4700 
4701 TDateTime TTrain::GetTrainTime(int Caller, TDateTime Time)
4702 {
4703  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetTrainTime," + Utilities->Format96HHMMSS(Time));
4704  TDateTime RepeatTime = TrainController->GetRepeatTime(1, Time, RepeatNumber, IncrementalMinutes);
4705 
4706  Utilities->CallLogPop(1453);
4707  return RepeatTime;
4708 }
4709 
4710 // ---------------------------------------------------------------------------
4711 
4712 bool TTrain::IsThereAnAdjacentTrain(int Caller, TTrain *&TrainToBeJoinedBy)
4713 { // Used to check for a stopped adjacent train for use in PopUp menu //new at v2.4.0
4714  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsThereAnAdjacentTrain" + "," + HeadCode);
4715  // check if there's a stopped adjacent train, if there is but not under sig control give a message in calling function
4716  // first check that train is fully on the railway
4717  bool FrontValid = false, RearValid = false;
4718  TTrackElement FrontAdjacentTrackElement, RearAdjacentTrackElement;
4719 
4720  if((LeadElement == -1) || (MidElement == -1))
4721  {
4722  TrainToBeJoinedBy = NULL;
4723  Utilities->CallLogPop(2131);
4724  return false;
4725  }
4727  {
4728  FrontAdjacentTrackElement = Track->TrackElementAt(965, (Track->TrackElementAt(966, LeadElement).Conn[LeadExitPos]));
4729  FrontValid = true;
4730  }
4732  {
4733  RearAdjacentTrackElement = Track->TrackElementAt(968, (Track->TrackElementAt(969, MidElement).Conn[MidEntryPos]));
4734  RearValid = true;
4735  }
4736  int TrainToBeJoinedByID = -1;
4737 
4738  // first check if on a 2-track element & select correct ID number if so
4739  if(FrontValid)
4740  {
4741  if(FrontAdjacentTrackElement.TrackType == Bridge)
4742  {
4744  {
4745  TrainToBeJoinedByID = FrontAdjacentTrackElement.TrainIDOnBridgeTrackPos23;
4746  }
4747  else
4748  {
4749  TrainToBeJoinedByID = FrontAdjacentTrackElement.TrainIDOnBridgeTrackPos01;
4750  }
4751  }
4752  else
4753  {
4754  TrainToBeJoinedByID = FrontAdjacentTrackElement.TrainIDOnElement;
4755  }
4756  }
4757  if((TrainToBeJoinedByID < 0) && RearValid)
4758  {
4759  // first check if on a 2-track element & select correct ID number if so
4760  if(RearAdjacentTrackElement.TrackType == Bridge)
4761  {
4763  {
4764  TrainToBeJoinedByID = RearAdjacentTrackElement.TrainIDOnBridgeTrackPos23;
4765  }
4766  else
4767  {
4768  TrainToBeJoinedByID = RearAdjacentTrackElement.TrainIDOnBridgeTrackPos01;
4769  }
4770  }
4771  else
4772  {
4773  TrainToBeJoinedByID = RearAdjacentTrackElement.TrainIDOnElement;
4774  }
4775  }
4776  if(TrainToBeJoinedByID < 0) // no adjacent train
4777  {
4778  TrainToBeJoinedBy = NULL;
4779  Utilities->CallLogPop(2132);
4780  return false;
4781  }
4782  TrainToBeJoinedBy = &(TrainController->TrainVectorAtIdent(44, TrainToBeJoinedByID));
4783  if(!TrainToBeJoinedBy->Stopped())
4784  {
4785  TrainToBeJoinedBy = NULL;
4786  Utilities->CallLogPop(2133);
4787  return false;
4788  }
4789  Utilities->CallLogPop(2134);
4790  return true;
4791 }
4792 
4793 // ---------------------------------------------------------------------------
4794 
4795 void TTrain::LogAction(int Caller, AnsiString OwnHeadCode, AnsiString OtherHeadCode, TActionType ActionType, AnsiString LocationName,
4796  TDateTime TimetableNonRepeatTime, bool Warning)
4797 /*
4798  Time = timetable time, the time adjustments for repeat trains is carried out internally
4799  Not all messages need this, if not needed a dummy value is required but not used
4800 
4801  Arrive: 06:05:40: 2F46 arrived at Old Street 1 minute late
4802  Pass: 06:05:40: 2F46 passed Old Street 1 minute late
4803  Terminate: 06:05:40: 2F46 terminated at Old Street 1 minute late
4804  //NB for Frh just give terminated message but without event time - don't use this function
4805  Depart: 06:05:15: 3F43 departed from Essex Road 2 minutes late
4806  Create: 06:05:40: 2F46 created at Old Street 1 minute late
4807  Enter: 06:05:40: 2F46 entered railway at Old Street 1 minute late
4808  Leave: 06:05:40: 2F46 left railway at 57-N4 1 minute late
4809  FrontSplit: 06:05:40: 2F46 split from front to 3D54 at Old Street 1 minute late
4810  RearSplit: 06:05:40: 2F46 split from rear to 3D54 at Old Street 1 minute late
4811  JoinedByOther: 06:05:40: 2F46 joined by 3D54 at Old Street 1 minute late
4812  ChangeDirection: 06:05:40: 2F46 changed direction at Old Street 1 minute late
4813  NewService: 06:05:40: 2F46 became new service 3D54 at Old Street 1 minute late
4814  TakeManualControl: 06:05:40: 2F46 taken under signaller control at Old Street
4815  RestoreTimetableControl: 06:05:40: 2F46 restored to timetable control at Old Street
4816  RemoveTrain: 06:05:40: 2F46 REMOVED FROM RAILWAY DUE TO CRASH at Old Street
4817  RemoveTrain: 06:05:40: 2F46 REMOVED FROM RAILWAY DUE TO DERAILMENT at Old Street
4818  RemoveTrain: 06:05:40: 2F46 REMOVED FROM RAILWAY at Old Street
4819  SignallerMoveForwards 06:05:40: 2F46 received signaller authority to proceed
4820  SignallerChangeDirection 06:05:40: 2F46 changed direction under signaller control at Old Street
4821  SignallerPassRedSignal 06:05:40: 2F46 received signaller authority to pass red signal
4822  SignallerJoin 06:05:40: 2F46 joined under signaller control by 3D54 at Old Street //new at v2.4.0
4823  TrainFailure 06:05:40: 2F46 suffered an onboard power failure at Old Street //new at v2.4.0
4824  RepairFailedTrain 06:05:40: 2F46 failure repaired at Old Street //new at v2.4.0
4825  SignallerControlStop 06:05:40: 2F46 received signaller instruction to stop
4826  SignallerStop 06:05:40: 2F46 stopped on signaller command
4827  SignallerLeave: 06:05:40: 2F46 left railway under signaller control at 57-N4
4828  SignallerStepForward: 06:05:40: 2F46 received signaller authority to step forward
4829 */ {
4830  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LogAction," + OwnHeadCode + "," + OtherHeadCode + "," +
4831  AnsiString(ActionType) + "," + LocationName + "," + HeadCode);
4832  AnsiString BaseLog = "", WarningBaseLog = "", PerfLog = "", ActionLog = "";
4833  int IntMinsLate = 0;
4834 
4835  // need to set it in case MinsLate == 0, since it isn't tested for that
4836  if(ActionType == Arrive)
4837  ActionLog = " arrived at ";
4838  if(ActionType == Terminate)
4839  {
4840  if(TerminatedMessageSent) // to avoid it being sent twice
4841  {
4842  Utilities->CallLogPop(1104);
4843  return;
4844  }
4845  ActionLog = " terminated at ";
4846  TerminatedMessageSent = true;
4847  }
4848  if(ActionType == Depart)
4849  ActionLog = " departed from ";
4850  if(ActionType == Pass)
4851  ActionLog = " passed ";
4852  if(ActionType == Create)
4853  ActionLog = " created at ";
4854  if(ActionType == Enter)
4855  ActionLog = " entered railway at ";
4856  if(ActionType == Leave)
4857  ActionLog = " left railway at ";
4858  if(ActionType == FrontSplit)
4859  ActionLog = " split from front to ";
4860  if(ActionType == RearSplit)
4861  ActionLog = " split from rear to ";
4862  if(ActionType == JoinedByOther)
4863  ActionLog = " joined by ";
4864  if(ActionType == ChangeDirection)
4865  ActionLog = " changed direction at ";
4866  if(ActionType == NewService)
4867  ActionLog = " became new service ";
4868  if(ActionType == TakeSignallerControl)
4869  ActionLog = " taken under signaller control at ";
4870  if(ActionType == RestoreTimetableControl)
4871  ActionLog = " restored to timetable control at ";
4872  if(ActionType == RemoveTrain)
4873  {
4874  if(Crashed)
4875  ActionLog = " REMOVED FROM RAILWAY DUE TO CRASH at ";
4876  else if(Derailed)
4877  ActionLog = " REMOVED FROM RAILWAY DUE TO DERAILMENT at ";
4878  else
4879  ActionLog = " REMOVED FROM RAILWAY at ";
4880  }
4881  if(ActionType == SignallerMoveForwards)
4882  ActionLog = " received signaller authority to proceed";
4883  if(ActionType == SignallerStepForward)
4884  ActionLog = " received signaller authority to step forward";
4885  if(ActionType == SignallerChangeDirection)
4886  ActionLog = " changed direction under signaller control at ";
4887  if(ActionType == SignallerPassRedSignal)
4888  ActionLog = " received signaller authority to pass red signal";
4889  if(ActionType == SignallerControlStop)
4890  ActionLog = " received signaller instruction to stop";
4891  if(ActionType == SignallerStop)
4892  ActionLog = " stopped on signaller instruction ";
4893  if(ActionType == SignallerJoin)
4894  ActionLog = " joined under signaller control by ";
4895  if(ActionType == TrainFailure)
4896  ActionLog = " suffered an onboard power failure at ";
4897  if(ActionType == RepairFailedTrain)
4898  ActionLog = " failure repaired at ";
4899  if(ActionType == SignallerLeave)
4900  ActionLog = " left railway under signaller control at ";
4901  if(OtherHeadCode != "")
4902  OtherHeadCode += " at ";
4903  TDateTime ActualTime = TrainController->TTClockTime;
4904 
4905  if(Warning)
4906  {
4907  BaseLog = Utilities->Format96HHMMSS(ActualTime) + " WARNING: " + HeadCode + ActionLog + OtherHeadCode + LocationName;
4908  WarningBaseLog = HeadCode + ActionLog + OtherHeadCode + LocationName;
4909  }
4910  else
4911  {
4912  BaseLog = Utilities->Format96HHMMSS(ActualTime) + ": " + HeadCode + ActionLog + OtherHeadCode + LocationName;
4913  }
4914  bool TimePerformance = true;
4915 
4916  if((ActionType == TakeSignallerControl) || (ActionType == RestoreTimetableControl) || (ActionType == RemoveTrain) || (ActionType == SignallerMoveForwards)
4917  || (ActionType == SignallerChangeDirection) || (ActionType == SignallerPassRedSignal) || (ActionType == SignallerControlStop) ||
4918  (ActionType == SignallerStop) || (ActionType == SignallerLeave) || (ActionType == SignallerStepForward) || (ActionType == SignallerJoin) ||
4919  (ActionType == TrainFailure) || (ActionType == RepairFailedTrain))
4920  // SignallerJoin & RepairFailedTrain new at v2.4.0
4921  {
4922  TimePerformance = false;
4923  }
4924  if(TimePerformance)
4925  {
4926  double MinsLate = ((double)(ActualTime - GetTrainTime(1, TimetableNonRepeatTime))) * 1440;
4927  MinsDelayed = float(MinsLate);
4928  // new v2.2.0 for OpActionPanel, can be positive or negative
4929  if(ActionType == Arrive)
4931  // since train has just arrived this value is the
4932  // recoverable time available at this stop, so reduce MinsDelayed by this amount to prevent it being
4933  // subtracted from later stop recoverable times.
4934  if(MinsLate < 0)
4935  {
4936  IntMinsLate = int(ceil(MinsLate));
4937  }
4938  if(MinsLate > 0)
4939  {
4940  IntMinsLate = int(floor(MinsLate));
4941  }
4942  if(IntMinsLate == 0)
4943  {
4944  PerfLog = " on time";
4945  }
4946  else if(IntMinsLate == 1)
4947  PerfLog = " 1 minute late";
4948  else if(IntMinsLate == -1)
4949  PerfLog = " 1 minute early";
4950  else if(IntMinsLate > 1)
4951  PerfLog = " " + AnsiString(IntMinsLate) + " minutes late";
4952  else if(IntMinsLate < -1)
4953  {
4954  int PosIntMinsLate = -IntMinsLate;
4955  PerfLog = " " + AnsiString(PosIntMinsLate) + " minutes early";
4956  }
4957  if(LocationName.Pos('-') > 0)
4958  {
4959  PerfLog = "," + PerfLog;
4960  // if a position add a comma to separate vertical position number from number of minutes (better appearance)
4961  }
4962  Display->PerformanceLog(0, BaseLog + PerfLog);
4963  }
4964  else
4965  Display->PerformanceLog(1, BaseLog);
4966  if(Warning)
4967  {
4968  Display->WarningLog(0, WarningBaseLog);
4969  }
4970 
4971  // update statistics
4972  if((ActionType == Arrive) && (IntMinsLate == 0))
4973  {
4975  }
4976  else if((ActionType == Arrive) && (IntMinsLate > 0))
4977  {
4979  TrainController->TotLateArrMins += IntMinsLate;
4980  }
4981  else if((ActionType == Arrive) && (IntMinsLate < 0))
4982  {
4984  TrainController->TotEarlyArrMins += abs(IntMinsLate);
4985  }
4986  else if((ActionType == Pass) && (IntMinsLate == 0))
4987  {
4989  }
4990  else if((ActionType == Pass) && (IntMinsLate > 0))
4991  {
4993  TrainController->TotLatePassMins += IntMinsLate;
4994  }
4995  else if((ActionType == Pass) && (IntMinsLate < 0))
4996  {
4998  TrainController->TotEarlyPassMins += abs(IntMinsLate);
4999  }
5000  else if((ActionType == Depart) && (IntMinsLate == 0))
5001  {
5003  }
5004  else if((ActionType == Depart) && (IntMinsLate > 0))
5005  {
5007  TrainController->TotLateDepMins += IntMinsLate;
5008  }
5009  Utilities->CallLogPop(968);
5010 }
5011 
5012 // ---------------------------------------------------------------------------
5013 
5014 void TTrain::TrainHasFailed(int Caller)
5015 {
5016  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainHasFailed," + HeadCode);
5017  if(Crashed || Derailed || DerailPending)
5018  {
5019  TrainFailurePending = false;
5020  Utilities->CallLogPop(2135);
5021  return;
5022  }
5023  AnsiString LocName = "";
5024 
5025  if(LeadElement > -1)
5026  {
5028  }
5029  if((LocName == "") && (MidElement > -1))
5030  {
5032  }
5033  if((LocName == "") && LeadElement > -1)
5034  {
5035  LocName = Track->TrackElementAt(974, LeadElement).ElementID;
5036  }
5037  if((LocName == "") && (MidElement > -1))
5038  {
5039  LocName = Track->TrackElementAt(975, MidElement).ElementID;
5040  }
5041  TrainController->StopTTClockMessage(81, HeadCode + " has suffered an onboard power failure at " + LocName);
5042  TrainFailed = true;
5043  TrainFailurePending = false;
5045  PowerAtRail = 0.08;
5046  AValue = sqrt(2 * PowerAtRail / Mass);
5048  // TrainFailed only called when PlotElements properly set to lead, Mid & Lag elements
5049  if(Stopped())
5050  {
5051  EntrySpeed = 0;
5052  ExitSpeedHalf = 0;
5053  ExitSpeedFull = 0;
5054  MaxExitSpeed = 0;
5055  BrakeRate = 0;
5056  StoppedWithoutPower = true;
5057  }
5059  LogAction(33, HeadCode, "", TrainFailure, LocName, TDateTime(0), true);
5060  // true for warning, TDateTime not used
5061  Utilities->CallLogPop(2136);
5062 }
5063 
5064 // ---------------------------------------------------------------------------
5065 
5066 void TTrain::FrontTrainSplit(int Caller)
5067 {
5068 /*
5069  Split logic is:- at least one of 4 final train positions must overlap with one of original train positions, & final 4 positions
5070  will maximise the number at the location. Note that this function isn't sophisticated enough to account for trains already at the
5071  location in determining the 4 positions, and will give a failure message if a train obstructs any of the 4 positions. In these
5072  circumstances the other train will need to be moved sufficiently away to release all 4 positions, then the train will split.
5073 */
5074  TrainController->LogEvent("" + AnsiString(Caller) + ",FrontTrainSplit" + "," + HeadCode);
5075  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",FrontTrainSplit" + "," + HeadCode);
5076  if(PowerAtRail < 1)
5077  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can split when power restored
5078  {
5080  TrainController->StopTTClockMessage(82, HeadCode + ": A train without power can't split");
5082  Utilities->CallLogPop(2137);
5083  return;
5084  }
5085  AnsiString LocationName = Track->TrackElementAt(555, LeadElement).ActiveTrackElementName;
5086 
5087  if(LocationName == "")
5088  LocationName = Track->TrackElementAt(837, MidElement).ActiveTrackElementName;
5089  int FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos;
5090  int RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos;
5091  int FrontTrainRearPosition, FrontTrainFrontPosition, FrontTrainExitPos;
5093 
5094  // determine all positions & exits
5095  if(LocationName != "")
5096  {
5097  // if message given only call at ~5 sec intervals
5099  {
5100  FirstNamedElementPos = LeadElement;
5101  if(!Track->ThisNamedLocationLongEnoughForSplit(0, LocationName, FirstNamedElementPos,
5102  // check if possible with LeadElement as First
5103  SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos))
5104  {
5105  FirstNamedElementPos = MidElement;
5106  if(!Track->ThisNamedLocationLongEnoughForSplit(1, LocationName, FirstNamedElementPos,
5107  // if not then accept second if possible (though if Lead no good hard to see how Mid could work, but leave in)
5108  SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos))
5109  {
5111  {
5112  TrainController->LogActionError(6, HeadCode, "", FailLocTooShort, LocationName);
5114  }
5115  Utilities->CallLogPop(1009);
5116  return;
5117  }
5118  }
5119  else
5120  {
5121  // if first is possible then check if all 4 positions at location, and if not try the second
5122  int LeadPosA = FirstNamedElementPos;
5123  int LeadPosB = FirstNamedLinkedElementPos;
5124  int LeadPosC = SecondNamedElementPos;
5125  int LeadPosD = SecondNamedLinkedElementPos;
5126  // count number of positions that are at the location
5127  int LeadNumAtLoc = 0;
5128  if(Track->TrackElementAt(758, LeadPosA).ActiveTrackElementName == LocationName)
5129  LeadNumAtLoc++;
5130  if(Track->TrackElementAt(759, LeadPosB).ActiveTrackElementName == LocationName)
5131  LeadNumAtLoc++;
5132  if(Track->TrackElementAt(760, LeadPosC).ActiveTrackElementName == LocationName)
5133  LeadNumAtLoc++;
5134  if(Track->TrackElementAt(761, LeadPosD).ActiveTrackElementName == LocationName)
5135  LeadNumAtLoc++;
5136  if(LeadNumAtLoc < 4)
5137  {
5138  FirstNamedElementPos = MidElement;
5139  if(!Track->ThisNamedLocationLongEnoughForSplit(4, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
5140  SecondNamedLinkedElementPos)) // restore originals
5141  {
5142  FirstNamedElementPos = LeadPosA;
5143  FirstNamedLinkedElementPos = LeadPosB;
5144  SecondNamedElementPos = LeadPosC;
5145  SecondNamedLinkedElementPos = LeadPosD;
5146  }
5147  else // count number at location
5148  {
5149  int MidNumAtLoc = 0;
5150  if(Track->TrackElementAt(762, FirstNamedElementPos).ActiveTrackElementName == LocationName)
5151  MidNumAtLoc++;
5152  if(Track->TrackElementAt(763, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5153  MidNumAtLoc++;
5154  if(Track->TrackElementAt(764, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5155  MidNumAtLoc++;
5156  if(Track->TrackElementAt(765, SecondNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5157  MidNumAtLoc++;
5158  if(LeadNumAtLoc > MidNumAtLoc)
5159  // change back, else keep new values
5160  {
5161  FirstNamedElementPos = LeadPosA;
5162  FirstNamedLinkedElementPos = LeadPosB;
5163  SecondNamedElementPos = LeadPosC;
5164  SecondNamedLinkedElementPos = LeadPosD;
5165  }
5166  }
5167  }
5168  }
5169  }
5170  else
5171  {
5172  Utilities->CallLogPop(1791);
5173  return;
5174  }
5175  }
5176  else
5177  throw Exception("Error - LocationName not set in FrontTrainSplit");
5178  // set front & rear train parameters
5179  // need RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos, FrontTrainRearPosition, FrontTrainFrontPosition & FrontTrainExitPos;
5180  // have LeadElement & MidElement of train defining its direction, & one or other on FirstNamedElementPos
5181  // have FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos & SecondNamedLinkedElementPos from ThisNamedLocationLongEnoughForSplit.
5182  if(LeadElement == FirstNamedElementPos)
5183  {
5184  if(MidElement == SecondNamedElementPos)
5185  {
5186  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
5187  FrontTrainRearPosition = FirstNamedElementPos;
5188  RearTrainFrontPosition = SecondNamedElementPos;
5189  RearTrainRearPosition = SecondNamedLinkedElementPos;
5190  }
5191  else // MidElement must == FirstNamedLinkedElementPos
5192  {
5193  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
5194  FrontTrainRearPosition = SecondNamedElementPos;
5195  RearTrainFrontPosition = FirstNamedElementPos;
5196  RearTrainRearPosition = FirstNamedLinkedElementPos;
5197  }
5198  }
5199  else // MidElement == FirstNamedElementPos
5200  {
5201  if(LeadElement == SecondNamedElementPos)
5202  {
5203  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
5204  FrontTrainRearPosition = SecondNamedElementPos;
5205  RearTrainFrontPosition = FirstNamedElementPos;
5206  RearTrainRearPosition = FirstNamedLinkedElementPos;
5207  }
5208  else // LeadElement must == FirstNamedLinkedElementPos
5209  {
5210  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
5211  FrontTrainRearPosition = FirstNamedElementPos;
5212  RearTrainFrontPosition = SecondNamedElementPos;
5213  RearTrainRearPosition = SecondNamedLinkedElementPos;
5214  }
5215  }
5216  RearTrainExitPos = -1;
5217  for(int x = 0; x < 4; x++)
5218  {
5219  if(Track->TrackElementAt(584, RearTrainRearPosition).Conn[x] == RearTrainFrontPosition)
5220  {
5221  RearTrainExitPos = x;
5222  break;
5223  }
5224  }
5225  if(RearTrainExitPos == -1)
5226  throw Exception("Error - RearTrainRearPosition not linked to RearTrainFrontPosition in FrontTrainSplit");
5227 
5228  FrontTrainExitPos = -1;
5229  for(int x = 0; x < 4; x++)
5230  {
5231  if(Track->TrackElementAt(585, FrontTrainRearPosition).Conn[x] == FrontTrainFrontPosition)
5232  {
5233  FrontTrainExitPos = x;
5234  break;
5235  }
5236  }
5237  if(FrontTrainExitPos == -1)
5238  throw Exception("Error - FrontTrainRearPosition not linked to FrontTrainFrontPosition in FrontTrainSplit");
5239 
5240  // check no train (apart from self) on any of the 4 elements & fail if so
5241  int TrainIDOnRearOfRearTrain, TrainIDOnFrontOfRearTrain, TrainIDOnRearOfFrontTrain, TrainIDOnFrontOfFrontTrain;
5242  TTrackElement RearMostElement = Track->TrackElementAt(574, RearTrainRearPosition);
5243 
5244  if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos > 1))
5245  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeTrackPos23;
5246  else if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos < 2))
5247  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeTrackPos01;
5248  else
5249  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnElement;
5250  // RearTrainFrontPosition = RearMostElement.Conn[RearTrainExitPos];
5251  TrainIDOnFrontOfRearTrain = Track->TrackElementAt(575, RearTrainFrontPosition).TrainIDOnElement;
5252  // can't be a bridge
5253  TrainIDOnRearOfFrontTrain = Track->TrackElementAt(576, FrontTrainRearPosition).TrainIDOnElement;
5254  // can't be a bridge
5255  // FrontTrainFrontPosition = Track->TrackElementAt(578,FrontTrainRearPosition).Conn[FrontTrainExitPos];
5256  TTrackElement FrontMostElement = Track->TrackElementAt(577, FrontTrainFrontPosition);
5257 
5258  if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos > 1))
5259  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeTrackPos23;
5260  else if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos < 2))
5261  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeTrackPos01;
5262  else
5263  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnElement;
5264 
5265  if(((TrainIDOnRearOfRearTrain > -1) && (TrainIDOnRearOfRearTrain != TrainID)) || ((TrainIDOnFrontOfRearTrain > -1) && (TrainIDOnFrontOfRearTrain != TrainID)
5266  ) || ((TrainIDOnRearOfFrontTrain > -1) && (TrainIDOnRearOfFrontTrain != TrainID)) ||
5267  ((TrainIDOnFrontOfFrontTrain > -1) && (TrainIDOnFrontOfFrontTrain != TrainID)))
5268  {
5270  {
5273  }
5274  // don't advance ActionVectorEntryPtr as need to keep trying, other train may move off eventually
5275  Utilities->CallLogPop(1010);
5276  return;
5277  }
5278 
5280  {
5282  }
5283  // reposition existing rear train, need to do this first for 2 reasons - 1) will likely be in the way of the new front train, and 2)
5284  // the new train will likely cause a reallocation of the TrainVector, and if so the reference to the existing train will be invalidated.
5285  // Hence deal with existing train while it references a valid entry in the vector, but retain the Old ActionVectorEntryPtr in a separate
5286  // variable as it is needed for setting up the new train
5287  TActionVectorEntry *OldActionVectorEntryPtr = ActionVectorEntryPtr;
5288 
5289  UnplotTrain(0);
5290  StartSpeed = 0;
5291  RearStartElement = RearTrainRearPosition;
5292  RearStartExitPos = RearTrainExitPos;
5293  StoppedAtLocation = true;
5294  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
5295  {
5296  StoppedWithoutPower = true;
5297  }
5298  PlotStartPosition(3);
5303 
5304  Mass = Mass / 2;
5305  MaxBrakeRate = MaxBrakeRate / 2;
5306  PowerAtRail = PowerAtRail / 2;
5307  AValue = sqrt(2 * PowerAtRail / Mass);
5308  // shouldn't change but include in case not set earlier
5309 
5310  // create new front train
5311 /*
5312  TrainController::AddTrain(int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass,
5313  double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr,
5314  int RepeatNumber, int IncrementalMinutes, int SignallerSpeed)
5315 */
5316  // same Mass, MaxBrakeRate & PowerAtRail as this train's halved values, and same MaxRunningSpeed as this train
5317  TActionEventType EventType = NoEvent;
5318 
5319  if(!TrainController->AddTrain(0, FrontTrainRearPosition, FrontTrainFrontPosition, OtherHeadCode, 0, Mass, MaxRunningSpeed, MaxBrakeRate, PowerAtRail,
5320  "Timetable", OldActionVectorEntryPtr->LinkedTrainEntryPtr, RepeatNumber, IncrementalMinutes, IncrementalDigits, SignallerMaxSpeed, false, EventType))
5321  // false for SignallerControl
5322  {
5323  Utilities->CallLogPop(1721); // EventType not used here
5324  // if fails either a throw will have been sent in AddTrain or start position failed prob because of
5325  // another train, in which case a message will have been sent to the perf log, also might well clear later
5326  // when other train moves away
5327  return;
5328  }
5329  // Note data in 'this' now probably invalid as there has been a new addition to the TrainVector, so the train is likely to have a new address, hence make no more changes for the current train
5330  // see mods in UpdateTrain for v1.3.2
5331  TrainController->TrainAdded = true;
5332 
5333  TTrainOperatingData &TTOD = OldActionVectorEntryPtr->LinkedTrainEntryPtr->TrainOperatingDataVector.at(RepeatNumber); // this is for the newly created train
5334 
5335  TTOD.TrainID = TrainController->TrainVector.back().TrainID;
5336  TTOD.RunningEntry = Running;
5337  Utilities->CallLogPop(998);
5338 }
5339 
5340 // ---------------------------------------------------------------------------
5341 
5342 void TTrain::RearTrainSplit(int Caller)
5343 {
5344 /*
5345  Split logic is:- at least one of 4 final train positions must overlap with one of original train positions, & final 4 positions
5346  will maximise the number at the location. Note that this function isn't sophisticated enough to account for trains already at the
5347  location in determining the 4 positions, and will give a failure message if a train obstructs any of the 4 positions. In these
5348  circumstances the other train will need to be moved sufficiently away to release all 4 positions, then the train will split.
5349 */
5350  TrainController->LogEvent("" + AnsiString(Caller) + ",RearTrainSplit" + "," + HeadCode);
5351  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RearTrainSplit" + "," + HeadCode);
5352  if(PowerAtRail < 1)
5353  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can split when power restored
5354  {
5356  TrainController->StopTTClockMessage(83, HeadCode + ": A train without power can't split");
5358  Utilities->CallLogPop(2138);
5359  return;
5360  }
5361  AnsiString LocationName = Track->TrackElementAt(587, LeadElement).ActiveTrackElementName;
5362 
5363  if(LocationName == "")
5364  LocationName = Track->TrackElementAt(838, MidElement).ActiveTrackElementName;
5365  int FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos;
5366  int RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos;
5367  int FrontTrainRearPosition, FrontTrainFrontPosition, FrontTrainExitPos;
5369 
5370  // determine all positions & exits
5371  if(LocationName != "")
5372  {
5373  // if message given only call at ~5 sec intervals
5375  {
5376  FirstNamedElementPos = LeadElement;
5377  if(!Track->ThisNamedLocationLongEnoughForSplit(2, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
5378  SecondNamedLinkedElementPos))
5379  {
5380  FirstNamedElementPos = MidElement;
5381  if(!Track->ThisNamedLocationLongEnoughForSplit(3, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
5382  SecondNamedLinkedElementPos))
5383  {
5385  {
5386  TrainController->LogActionError(9, HeadCode, "", FailLocTooShort, LocationName);
5388  }
5389  Utilities->CallLogPop(1013);
5390  return;
5391  }
5392  }
5393  else
5394  {
5395  // if first is possible then check if all 4 positions at location, and if not try the second
5396  int LeadPosA = FirstNamedElementPos;
5397  int LeadPosB = FirstNamedLinkedElementPos;
5398  int LeadPosC = SecondNamedElementPos;
5399  int LeadPosD = SecondNamedLinkedElementPos;
5400  // count number of positions that are at the location
5401  int LeadNumAtLoc = 0;
5402  if(Track->TrackElementAt(767, LeadPosA).ActiveTrackElementName == LocationName)
5403  LeadNumAtLoc++;
5404  if(Track->TrackElementAt(768, LeadPosB).ActiveTrackElementName == LocationName)
5405  LeadNumAtLoc++;
5406  if(Track->TrackElementAt(769, LeadPosC).ActiveTrackElementName == LocationName)
5407  LeadNumAtLoc++;
5408  if(Track->TrackElementAt(770, LeadPosD).ActiveTrackElementName == LocationName)
5409  LeadNumAtLoc++;
5410  if(LeadNumAtLoc < 4)
5411  {
5412  FirstNamedElementPos = MidElement;
5413  if(!Track->ThisNamedLocationLongEnoughForSplit(5, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
5414  SecondNamedLinkedElementPos)) // restore originals
5415  {
5416  FirstNamedElementPos = LeadPosA;
5417  FirstNamedLinkedElementPos = LeadPosB;
5418  SecondNamedElementPos = LeadPosC;
5419  SecondNamedLinkedElementPos = LeadPosD;
5420  }
5421  else // count number at location
5422  {
5423  int MidNumAtLoc = 0;
5424  if(Track->TrackElementAt(771, FirstNamedElementPos).ActiveTrackElementName == LocationName)
5425  MidNumAtLoc++;
5426  if(Track->TrackElementAt(772, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5427  MidNumAtLoc++;
5428  if(Track->TrackElementAt(773, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5429  MidNumAtLoc++;
5430  if(Track->TrackElementAt(774, SecondNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5431  MidNumAtLoc++;
5432  if(LeadNumAtLoc > MidNumAtLoc)
5433  // change back, else keep new values
5434  {
5435  FirstNamedElementPos = LeadPosA;
5436  FirstNamedLinkedElementPos = LeadPosB;
5437  SecondNamedElementPos = LeadPosC;
5438  SecondNamedLinkedElementPos = LeadPosD;
5439  }
5440  }
5441  }
5442  }
5443  }
5444  else
5445  {
5446  Utilities->CallLogPop(1792);
5447  return;
5448  }
5449  }
5450  else
5451  throw Exception("Error - LocationName not set in RearTrainSplit");
5452  // set front & rear train parameters
5453  // need RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos, FrontTrainRearPosition, FrontTrainFrontPosition & FrontTrainExitPos;
5454  // have LeadElement & MidElement of train defining its direction, & one or other on FirstNamedElementPos
5455  // have FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos & SecondNamedLinkedElementPos from ThisNamedLocationLongEnoughForSplit.
5456  if(LeadElement == FirstNamedElementPos)
5457  {
5458  if(MidElement == SecondNamedElementPos)
5459  {
5460  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
5461  FrontTrainRearPosition = FirstNamedElementPos;
5462  RearTrainFrontPosition = SecondNamedElementPos;
5463  RearTrainRearPosition = SecondNamedLinkedElementPos;
5464  }
5465  else // MidElement must == FirstNamedLinkedElementPos
5466  {
5467  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
5468  FrontTrainRearPosition = SecondNamedElementPos;
5469  RearTrainFrontPosition = FirstNamedElementPos;
5470  RearTrainRearPosition = FirstNamedLinkedElementPos;
5471  }
5472  }
5473  else // MidElement == FirstNamedElementPos
5474  {
5475  if(LeadElement == SecondNamedElementPos)
5476  {
5477  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
5478  FrontTrainRearPosition = SecondNamedElementPos;
5479  RearTrainFrontPosition = FirstNamedElementPos;
5480  RearTrainRearPosition = FirstNamedLinkedElementPos;
5481  }
5482  else // LeadElement must == FirstNamedLinkedElementPos
5483  {
5484  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
5485  FrontTrainRearPosition = FirstNamedElementPos;
5486  RearTrainFrontPosition = SecondNamedElementPos;
5487  RearTrainRearPosition = SecondNamedLinkedElementPos;
5488  }
5489  }
5490  RearTrainExitPos = -1;
5491  for(int x = 0; x < 4; x++)
5492  {
5493  if(Track->TrackElementAt(588, RearTrainRearPosition).Conn[x] == RearTrainFrontPosition)
5494  {
5495  RearTrainExitPos = x;
5496  break;
5497  }
5498  }
5499  if(RearTrainExitPos == -1)
5500  throw Exception("Error - RearTrainRearPosition not linked to RearTrainFrontPosition in RearTrainSplit");
5501  FrontTrainExitPos = -1;
5502  for(int x = 0; x < 4; x++)
5503  {
5504  if(Track->TrackElementAt(589, FrontTrainRearPosition).Conn[x] == FrontTrainFrontPosition)
5505  {
5506  FrontTrainExitPos = x;
5507  break;
5508  }
5509  }
5510  if(FrontTrainExitPos == -1)
5511  throw Exception("Error - FrontTrainRearPosition not linked to FrontTrainFrontPosition in RearTrainSplit");
5512 
5513  // check no train (apart from self) on any of the 4 elements & fail if so
5514  int TrainIDOnRearOfRearTrain, TrainIDOnFrontOfRearTrain, TrainIDOnRearOfFrontTrain, TrainIDOnFrontOfFrontTrain;
5515  TTrackElement RearMostElement = Track->TrackElementAt(590, RearTrainRearPosition);
5516 
5517  if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos > 1))
5518  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeTrackPos23;
5519  else if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos < 2))
5520  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeTrackPos01;
5521  else
5522  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnElement;
5523  // RearTrainFrontPosition = RearMostElement.Conn[RearTrainExitPos];
5524  TrainIDOnFrontOfRearTrain = Track->TrackElementAt(591, RearTrainFrontPosition).TrainIDOnElement;
5525  // can't be a bridge
5526  TrainIDOnRearOfFrontTrain = Track->TrackElementAt(592, FrontTrainRearPosition).TrainIDOnElement;
5527  // can't be a bridge
5528  // FrontTrainFrontPosition = Track->TrackElementAt(593,FrontTrainRearPosition).Conn[FrontTrainExitPos];
5529  TTrackElement FrontMostElement = Track->TrackElementAt(594, FrontTrainFrontPosition);
5530 
5531  if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos > 1))
5532  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeTrackPos23;
5533  else if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos < 2))
5534  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeTrackPos01;
5535  else
5536  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnElement;
5537 
5538  if(((TrainIDOnRearOfRearTrain > -1) && (TrainIDOnRearOfRearTrain != TrainID)) || ((TrainIDOnFrontOfRearTrain > -1) && (TrainIDOnFrontOfRearTrain != TrainID)
5539  ) || ((TrainIDOnRearOfFrontTrain > -1) && (TrainIDOnRearOfFrontTrain != TrainID)) ||
5540  ((TrainIDOnFrontOfFrontTrain > -1) && (TrainIDOnFrontOfFrontTrain != TrainID)))
5541  {
5543  {
5546  }
5547  // don't advance ActionVectorEntryPtr as need to keep trying, other train may move off eventually
5548  Utilities->CallLogPop(1014);
5549  return;
5550  }
5551 
5553  {
5555  }
5556 
5557  // reposition existing front train, need to do this first for 2 reasons - 1) will likely be in the way of the new rear train, and 2)
5558  // the new train will likely cause a reallocation of the TrainVector, and if so the reference to the existing train will be invalidated.
5559  // Hence deal with existing train while it references a valid entry in the vector, but retain the Old ActionVectorEntryPtr in a separate
5560  // variable as it is needed for setting up the new train
5561  TActionVectorEntry *OldActionVectorEntryPtr = ActionVectorEntryPtr;
5562 
5563  UnplotTrain(1);
5564  StartSpeed = 0;
5565  RearStartElement = FrontTrainRearPosition;
5566  RearStartExitPos = FrontTrainExitPos;
5567  StoppedAtLocation = true;
5568  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
5569  {
5570  StoppedWithoutPower = true;
5571  }
5572  PlotStartPosition(4);
5577  Mass = Mass / 2;
5578  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).Mass = Mass;
5579  MaxBrakeRate = MaxBrakeRate / 2;
5580  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).MaxBrakeRate = MaxBrakeRate;
5581  PowerAtRail = PowerAtRail / 2;
5582  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).PowerAtRail = PowerAtRail;
5583  AValue = sqrt(2 * PowerAtRail / Mass);
5584  // shouldn't change but include in case not set earlier
5585 
5586  // create new rear train
5587 /*
5588  TrainController::AddTrain(int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass,
5589  double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr,
5590  int RepeatNumber, int IncrementalMinutes)
5591 */
5592  // same Mass, MaxBrakeRate & PowerAtRail as this train's halved values, and same MaxRunningSpeed as this train
5593  TActionEventType EventType = NoEvent;
5594 
5595  if(!TrainController->AddTrain(1, RearTrainRearPosition, RearTrainFrontPosition, OtherHeadCode, 0, Mass, MaxRunningSpeed, MaxBrakeRate, PowerAtRail,
5596  "Timetable", OldActionVectorEntryPtr->LinkedTrainEntryPtr, RepeatNumber, IncrementalMinutes, IncrementalDigits, SignallerMaxSpeed, false, EventType))
5597  // false for SignallerControl
5598  {
5599  Utilities->CallLogPop(1722); // EventType not used here
5600  // if fails either a throw will have been sent in AddTrain or start position failed prob because of
5601  // another train, in which case a message will have been sent to the perf log, also might well clear later
5602  // when other train moves away
5603  return;
5604  }
5605  // Note data in 'this' now probably invalid as there has been a new addition to the TrainVector, so the train is likely to have a new address, hence make no more changes for the current train
5606  // see mods in UpdateTrain for v1.3.2
5607  TrainController->TrainAdded = true;
5608  TTrainOperatingData &TTOD = OldActionVectorEntryPtr->LinkedTrainEntryPtr->TrainOperatingDataVector.at(RepeatNumber); // this is for the newly created train
5609 
5610  TTOD.TrainID = TrainController->TrainVector.back().TrainID;
5611  TTOD.RunningEntry = Running;
5612  Utilities->CallLogPop(1015);
5613 }
5614 
5615 // ---------------------------------------------------------------------------
5616 
5617 void TTrain::FinishJoin(int Caller)
5618 {
5619  if(FinishJoinLogSent == false)
5620  {
5621  TrainController->LogEvent("" + AnsiString(Caller) + ",FinishJoin" + "," + HeadCode);
5622  FinishJoinLogSent = true; // so don't keep logging this event
5623  // don't need to reset it to false after the event as the train is deleted
5624  }
5625  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",FinishJoin" + "," + HeadCode);
5626  if(TrainFailed)
5627  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can join when repaired) Can FinishJoin if zero power & not failed, as for empty stock
5628  {
5630  TrainController->StopTTClockMessage(84, HeadCode + ": A failed train can't join another under timetable control");
5632  Utilities->CallLogPop(2139);
5633  return;
5634  }
5635  if(TrainGone)
5636  // this means that the train has already joined the other & is awaiting deletion by TrainController
5637  // without this the 'waiting' message can be given since the other train's ActionVectorEntryPtr has moved
5638  // on from jbo & TrainToJoinIsAdjacent returns false
5639  {
5640  Utilities->CallLogPop(1035);
5641  return;
5642  }
5643  TTrain *TrainToJoin;
5645 
5646  if(!TrainToJoinIsAdjacent(0, TrainToJoin))
5647  {
5649  {
5650  // Display->PerformanceLog(2, TrainController->TTClockTime.FormatString("hh:nn:ss") + ": " + HeadCode + " waiting to join " + JBOHeadCode + " at " + ActionVectorEntryPtr->LocationName);
5653  }
5654  Utilities->CallLogPop(1030);
5655  return; // keep this here in case need to add code before final return
5656  }
5657  // no need to clear error report flag here, cleared in jbo function
5658  // No need to set TimetableFinished, done in jbo function
5659  Utilities->CallLogPop(1031);
5660 }
5661 
5662 // ---------------------------------------------------------------------------
5663 
5664 void TTrain::JoinedBy(int Caller)
5665 {
5666  TrainController->LogEvent("" + AnsiString(Caller) + ",JoinedBy" + "," + HeadCode);
5667  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",JoinedBy" + "," + HeadCode);
5668  if(PowerAtRail < 1)
5669  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can join when power restored)
5670  {
5672  TrainController->StopTTClockMessage(85, HeadCode + ": A train without power can't be joined by another under timetable control");
5674  Utilities->CallLogPop(2140);
5675  return;
5676  }
5677  TTrain *TrainToBeJoinedBy;
5679 
5680  if(!TrainToBeJoinedByIsAdjacent(0, TrainToBeJoinedBy))
5681  {
5683  {
5684  // Display->PerformanceLog(3, TrainController->TTClockTime.FormatString("hh:nn:ss") + ": " + HeadCode + " waiting to be joined by " + FJOHeadCode + " at " + ActionVectorEntryPtr->LocationName);
5687  }
5688  LastActionDelayFlag = true;
5689  // need to update LastActionTime if this train first to arrive as need 30s after
5690  // both adjacent before the join
5691  Utilities->CallLogPop(1032);
5692  return;
5693  }
5694  // here when other train is adjacent
5696  {
5698  // need to update this as need 30s after both adjacent before the join
5699  LastActionDelayFlag = false;
5700  Utilities->CallLogPop(1033);
5701  return;
5702  }
5703  // here when other train is adjacent & 30 secs elapsed since both adjacent
5704 
5705  // set new values for mass etc
5706  if(MaxRunningSpeed > TrainToBeJoinedBy->MaxRunningSpeed)
5707  MaxRunningSpeed = TrainToBeJoinedBy->MaxRunningSpeed;
5708  double OtherBrakeForce = TrainToBeJoinedBy->MaxBrakeRate * TrainToBeJoinedBy->Mass;
5709  double OwnBrakeForce = MaxBrakeRate * Mass;
5710  double CombinedBrakeRate = (OtherBrakeForce + OwnBrakeForce) / (TrainToBeJoinedBy->Mass + Mass);
5711 
5712  Mass += TrainToBeJoinedBy->Mass;
5713  MaxBrakeRate = CombinedBrakeRate;
5714  PowerAtRail += TrainToBeJoinedBy->PowerAtRail;
5715  AValue = sqrt(2 * PowerAtRail / Mass);
5716 
5718  TrainToBeJoinedBy->TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported = NoEvent;
5719  TrainToBeJoinedBy->TimetableFinished = true;
5720  TrainToBeJoinedBy->TrainGone = true;
5721  // this will cause other train to be deleted
5722  TrainToBeJoinedBy->JoinedOtherTrainFlag = true;
5726  Utilities->CallLogPop(1034);
5727 }
5728 
5729 // ---------------------------------------------------------------------------
5730 
5732 {
5733  TrainController->LogEvent("" + AnsiString(Caller) + ",ChangeTrainDirection" + "," + HeadCode);
5734  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ChangeTrainDirection" + "," + HeadCode);
5735  if(PowerAtRail < 1)
5736  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can change direction when power restored)
5737  {
5739  TrainController->StopTTClockMessage(86, HeadCode + ": A train without power can't change direction under timetable control");
5740  ZeroPowerNoCDTMessage = true;
5741  Utilities->CallLogPop(2141);
5742  return;
5743  }
5744  TColor TempColour = BackgroundColour;
5745 
5746  UnplotTrain(2);
5749  StartSpeed = 0;
5750  StoppedAtLocation = true;
5751  PlotStartPosition(1);
5752  PlotTrainWithNewBackgroundColour(27, TempColour, Display);
5753  // plot same as was - should always be pale green
5757 
5758  //now erase a stub route if there is one, added at v2.5.1
5759  //first element of route is now immediately behind the train (i.e. next to MidElement)
5760  if(MidEntryPos >= 0)
5761  {
5762  TTrackElement MidTrackElement = Track->TrackElementAt(996, MidElement);
5763  int FirstRouteElementVecPos = MidTrackElement.Conn[MidEntryPos];
5764  int FirstRouteLinkPos = MidTrackElement.ConnLinkPos[MidEntryPos];
5765  int RouteNumber = -1;
5766  TAllRoutes::TRouteType RouteType = AllRoutes->GetRouteTypeAndNumber(34, FirstRouteElementVecPos, FirstRouteLinkPos, RouteNumber);
5767  if(RouteType == TAllRoutes::NotAutoSigsRoute)
5768  {
5769  TOneRoute &OR = AllRoutes->GetModifiableRouteAt(28, RouteNumber);
5770  TTrackElement TE = Track->TrackElementAt(997, FirstRouteElementVecPos);
5771  if((TE.TrackType != SignalPost) && (TE.TrackType != Continuation))//all autosigs routes have signalpost or continuation at 0 so they are automatically excluded
5772  {
5773  while(OR.PrefDirSize() > 0) //remove the route up to but not including the next facing signal, in case a pref dir route extends to another signal
5774  {
5775  TPrefDirElement PDE = OR.GetFixedPrefDirElementAt(247, 0); //these will change at each element removal because OR is a reference to the real route
5776  int TVPos2 = PDE.GetTrackVectorPosition();
5777  TTrackElement TE2 = Track->TrackElementAt(998, TVPos2);
5779  {
5780  AllRoutes->RemoveRouteElement(22, TE2.HLoc, TE2.VLoc, PDE.GetELink());
5781  }
5782  else
5783  {
5784  break;
5785  }
5786  }
5787  AllRoutes->RebuildRailwayFlag = true;
5788  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode, to replot without stub route
5789  }
5790  }
5791  }
5792  Utilities->CallLogPop(1012);
5793 }
5794 
5795 // ---------------------------------------------------------------------------
5796 
5797 void TTrain::NewTrainService(int Caller)
5798  // change to new train, give new service message
5799 {
5800  TrainController->LogEvent("" + AnsiString(Caller) + ",NewTrainService" + "," + HeadCode);
5801  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",NewTrainService" + "," + HeadCode);
5802  if(PowerAtRail < 1)
5803  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can form new service when power restored)
5804  {
5806  TrainController->StopTTClockMessage(87, HeadCode + ": A train without power can't form a new service");
5808  Utilities->CallLogPop(2142);
5809  return;
5810  }
5812 
5814  UnplotTrain(3);
5817  StartSpeed = 0;
5822  HeadCode = NewHeadCode;
5823  StoppedAtLocation = true;
5824  PlotStartPosition(5);
5826  // pale green
5829  TerminatedMessageSent = false;
5830  Utilities->CallLogPop(1022);
5831 }
5832 
5833 // ---------------------------------------------------------------------------
5834 
5835 void TTrain::RemainHere(int Caller)
5836 {
5837  Utilities->CallLog.push_back(Utilities->TimeStamp() + AnsiString(Caller) + ",RemainHere" + "," + HeadCode);
5838  if(RemainHereLogNotSent) // to prevent repeated logs
5839  {
5840  TrainController->LogEvent(Utilities->TimeStamp() + AnsiString(Caller) + ",RemainHere" + "," + HeadCode);
5841  RemainHereLogNotSent = false;
5842  }
5844  {
5845  Display->PerformanceLog(5, Utilities->Format96HHMMSS(TrainController->TTClockTime) + ": " + HeadCode + " terminated at " +
5848  TerminatedMessageSent = true;
5849  }
5850  TimetableFinished = true;
5851  Utilities->CallLogPop(1023);
5852 }
5853 
5854 // ---------------------------------------------------------------------------
5855 
5856 void TTrain::SendMissedActionLogs(int Caller, int IncNum, TActionVectorEntry *Ptr)
5857 /*
5858  Enter with pointer at next expected action, and IncNum the number by which have to increase the pointer
5859  to reach the action that is valid for the train's current position. i.e. IncNum error messages to be sent
5860  except where an action is a departure, starting at the current value for the pointer
5861  If IncNum is -1, then send messages for all remaining actions, including Fer if present
5862  If IncNum is -2, then send messages for all remaining actions, except Fer if present
5863 */ {
5864  if((Ptr->Command == "Snt") && Ptr->SignallerControl)
5865  return; // if remove train that starts under signaller control no messages needed
5866 
5867  TrainController->LogEvent("" + AnsiString(Caller) + ",SendMissedActionLogs," + AnsiString(IncNum) + "," + HeadCode);
5868  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SendMissedActionLogs," + AnsiString(IncNum) + "," + HeadCode);
5869  if(IncNum > 0)
5870  {
5871  for(int x = 0; x < IncNum; x++)
5872  {
5873  if(x > 0)
5874  Ptr++;
5875  // arrival - no need to test for termination as this section only covers missed actions up to the
5876  // arrival point - may terminate later but that not missed
5877  if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime > TDateTime(-1)))
5878  {
5880  }
5881  // arrival & departure
5882  if(Ptr->FormatType == TimeTimeLoc)
5883  {
5885  }
5886  // departure
5887  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
5888  {
5889  continue; // skip TimeLoc departures, message given for arrivals
5890  }
5891  // pass
5892  else if(Ptr->FormatType == PassTime)
5893  {
5895  }
5896  // split
5897  else if((Ptr->Command == "fsp") || (Ptr->Command == "rsp"))
5898  {
5900  }
5901  // jbo
5902  else if(Ptr->Command == "jbo")
5903  {
5905  }
5906  // Errors - have reached a station stop point (before a cdt) during Train->Update() so intervening actions can't
5907  // be starts, finishes or cdt
5908  else if((Ptr->Command == "Fns") || (Ptr->Command == "Frh") || (Ptr->Command == "Fer") || (Ptr->Command == "Fjo") || (Ptr->Command == "Snt") ||
5909  (Ptr->Command == "Sfs") || (Ptr->Command == "Snt-sh") || (Ptr->Command == "Sns") || (Ptr->Command == "Sns-sh") || (Ptr->Command == "Sns-fsh") ||
5910  (Ptr->Command == "cdt") || (Ptr->Command == "Frh-sh") || (Ptr->Command == "Fns-sh") || (Ptr->Command == "F-nshs") ||
5911  (Ptr->FormatType == Repeat))
5912  {
5913  throw Exception("Error - illegal command in SendMissedActionLogs for IncNum = " + AnsiString(IncNum) + ", and command = " + Ptr->Command);
5914  }
5915  }
5916  }
5917  else
5918  {
5919  bool IncludeFER = false;
5920  if(IncNum == -1)
5921  IncludeFER = true;
5922  while(true) // finish commands & repeats break out of loop
5923  {
5924  // Fer & excluded - send normal exit log to give minutes late or early - no, have already sent an unexpected exit message
5925  if(!IncludeFER && (Ptr->Command == "Fer"))
5926  {
5927  break;
5928  }
5929  // Fer & included
5930  else if(IncludeFER && (Ptr->Command == "Fer"))
5931  {
5933  break;
5934  }
5935  // Repeat
5936  else if(Ptr->FormatType == Repeat)
5937  {
5938  break;
5939  }
5940  // Fjo
5941  else if(Ptr->Command == "Fjo")
5942  {
5944  break;
5945  }
5946  // Frh
5947  else if(Ptr->Command == "Frh")
5948  {
5950  {
5952  TerminatedMessageSent = true;
5953  }
5954  break;
5955  }
5956  // Frh-sh
5957  else if(Ptr->Command == "Frh-sh")
5958  {
5960  {
5962  TerminatedMessageSent = true;
5963  }
5964  break;
5965  }
5966  // Fns, F-nshs, Fns-sh
5967  else if((Ptr->Command == "Fns") || (Ptr->Command == "F-nshs") || (Ptr->Command == "Fns-sh"))
5968  {
5970  break;
5971  }
5972  // end of breakout actions
5973  // arrival
5974  if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime > TDateTime(-1)))
5975  {
5976  if(IsTrainTerminating(1))
5977  {
5979  TerminatedMessageSent = true;
5980  }
5981  else
5982  {
5984  }
5985  }
5986  // arrival & departure
5987  else if(Ptr->FormatType == TimeTimeLoc)
5988  {
5990  }
5991  // departure
5992  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
5993  {
5994  Ptr++;
5995  continue; // skip TimeLoc departures, message given for arrivals
5996  }
5997  // pass
5998  else if(Ptr->FormatType == PassTime)
5999  {
6001  }
6002  // split
6003  else if((Ptr->Command == "fsp") || (Ptr->Command == "rsp"))
6004  {
6006  }
6007  // jbo
6008  else if(Ptr->Command == "jbo")
6009  {
6011  }
6012  // cdt
6013  else if(Ptr->Command == "cdt")
6014  {
6016  }
6017  // Errors
6018  else if((Ptr->Command == "Snt-sh") || (Ptr->Command == "Sfs") || (Ptr->Command == "Sns") || (Ptr->Command == "Sns-sh") ||
6019  (Ptr->Command == "Sns-fsh") || ((Ptr->Command == "Snt") && !Ptr->SignallerControl))
6020  {
6021  throw Exception("Error - illegal command in SendMissedActionLogs for IncNum = " + AnsiString(IncNum) + ", and command = " + Ptr->Command);
6022  }
6023  Ptr++;
6024  }
6025  TimetableFinished = true;
6026  }
6027  Utilities->CallLogPop(1021);
6028 }
6029 
6030 // ---------------------------------------------------------------------------
6031 
6032 bool TTrain::TrainToJoinIsAdjacent(int Caller, TTrain* &TrainToJoin)
6033  // ensure same repeatnumber
6034 {
6035  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainToJoinIsAdjacent" + "," + HeadCode);
6037 
6038  if(TrainToJoinTDEntry->TrainOperatingDataVector.at(RepeatNumber).RunningEntry != Running)
6039  {
6040  Utilities->CallLogPop(1024);
6041  return false;
6042  }
6043  TrainToJoin = &(TrainController->TrainVectorAtIdent(33, TrainToJoinTDEntry->TrainOperatingDataVector.at(RepeatNumber).TrainID));
6044  if(TrainToJoin->StoppedAtLocation && (TrainToJoin->TrainMode == Timetable) && (TrainToJoin->ActionVectorEntryPtr->Command == "jbo"))
6045  {
6046  if((Track->TrackElementAt(610, LeadElement).Conn[LeadExitPos] == TrainToJoin->LeadElement) || (Track->TrackElementAt(611,
6047  LeadElement).Conn[LeadExitPos] == TrainToJoin->MidElement) || (Track->TrackElementAt(612, MidElement).Conn[MidEntryPos] == TrainToJoin->LeadElement)
6048  || (Track->TrackElementAt(613, MidElement).Conn[MidEntryPos] == TrainToJoin->MidElement))
6049  {
6050  Utilities->CallLogPop(1025);
6051  return true;
6052  }
6053  }
6054  Utilities->CallLogPop(1026);
6055  return false;
6056 }
6057 
6058 // ---------------------------------------------------------------------------
6059 
6060 bool TTrain::TrainToBeJoinedByIsAdjacent(int Caller, TTrain* &TrainToBeJoinedBy)
6061  // ensure same repeatnumber
6062 {
6063  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainToBeJoinedByIsAdjacent" + "," + HeadCode);
6064  TTrainDataEntry *TrainToBeJoinedByTDEntry = ActionVectorEntryPtr->LinkedTrainEntryPtr;
6065 
6066  if(TrainToBeJoinedByTDEntry->TrainOperatingDataVector.at(RepeatNumber).RunningEntry != Running)
6067  {
6068  Utilities->CallLogPop(1027);
6069  return false;
6070  }
6071  TrainToBeJoinedBy = &(TrainController->TrainVectorAtIdent(15, TrainToBeJoinedByTDEntry->TrainOperatingDataVector.at(RepeatNumber).TrainID));
6072  if(TrainToBeJoinedBy->StoppedAtLocation && (TrainToBeJoinedBy->TrainMode == Timetable) && (TrainToBeJoinedBy->ActionVectorEntryPtr->Command == "Fjo"))
6073  {
6074  if((Track->TrackElementAt(614, LeadElement).Conn[LeadExitPos] == TrainToBeJoinedBy->LeadElement) || (Track->TrackElementAt(615,
6075  LeadElement).Conn[LeadExitPos] == TrainToBeJoinedBy->MidElement) || (Track->TrackElementAt(616,
6076  MidElement).Conn[MidEntryPos] == TrainToBeJoinedBy->LeadElement) || (Track->TrackElementAt(617,
6077  MidElement).Conn[MidEntryPos] == TrainToBeJoinedBy->MidElement))
6078  {
6079  Utilities->CallLogPop(1028);
6080  return true;
6081  }
6082  }
6083  Utilities->CallLogPop(1029);
6084  return false;
6085 }
6086 
6087 // ---------------------------------------------------------------------------
6088 
6090 {
6091  TrainController->LogEvent("" + AnsiString(Caller) + ",NewShuttleFromNonRepeatService" + "," + HeadCode);
6092  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",NewShuttleFromNonRepeatService" + "," + HeadCode);
6093  if(PowerAtRail < 1)
6094  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can for new service when power restored)
6095  {
6097  TrainController->StopTTClockMessage(88, HeadCode + ": A train without power can't form a new service");
6099  Utilities->CallLogPop(2143);
6100  return;
6101  }
6102  AnsiString NewHeadCode = ActionVectorEntryPtr->NonRepeatingShuttleLinkHeadCode;
6103 
6105  UnplotTrain(4);
6108  StartSpeed = 0;
6113  HeadCode = NewHeadCode;
6114  IncrementalMinutes = TrainDataEntryPtr->ActionVector.back().RearStartOrRepeatMins;
6115  IncrementalDigits = TrainDataEntryPtr->ActionVector.back().FrontStartOrRepeatDigits;
6116  StoppedAtLocation = true;
6117  PlotStartPosition(6);
6119  // pale green
6122  TerminatedMessageSent = false;
6123  Utilities->CallLogPop(1078);
6124 }
6125 
6126 // ---------------------------------------------------------------------------
6127 
6129  // need to check whether all repeats finished or not
6130 {
6131  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RepeatShuttleOrRemainHere" + "," + HeadCode);
6132  if(RemainHereLogNotSent) // to prevent repeated logs
6133  {
6134  TrainController->LogEvent("" + AnsiString(Caller) + ",RepeatShuttleOrRemainHere" + "," + HeadCode);
6135  RemainHereLogNotSent = false;
6136  }
6138  // finished all repeats
6139  {
6141  {
6144  TerminatedMessageSent = true;
6145  // no need to clear message as no more actions
6146  }
6147  TimetableFinished = true;
6148  Utilities->CallLogPop(1080);
6149  return;
6150  }
6151  if(PowerAtRail < 1)
6152  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can for new service when power restored)
6153  {
6155  TrainController->StopTTClockMessage(89, HeadCode + ": A train without power can't form a new service");
6157  Utilities->CallLogPop(2144);
6158  return;
6159  }
6160  int TempRepeatNumber = RepeatNumber + 1;
6161  // need the next repeat value in order to obtain a correct NewHeadCode, but don't increase it
6162  // until after LogAction or the wrong time will be used
6163  AnsiString NewHeadCode = TrainController->GetRepeatHeadCode(6, ActionVectorEntryPtr->OtherHeadCode, TempRepeatNumber, IncrementalDigits);
6164 
6166  RepeatNumber++;
6167  UnplotTrain(5);
6170  StartSpeed = 0;
6175  HeadCode = NewHeadCode;
6176  StoppedAtLocation = true;
6177  PlotStartPosition(7);
6179  // pale green
6182  TerminatedMessageSent = false;
6183  Utilities->CallLogPop(1079);
6184 }
6185 
6186 // ---------------------------------------------------------------------------
6187 
6189 {
6190  TrainController->LogEvent("" + AnsiString(Caller) + ",RepeatShuttleOrNewNonRepeatService" + "," + HeadCode);
6191  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RepeatShuttleOrNewNonRepeatService" + "," + HeadCode);
6192  if(PowerAtRail < 1)
6193  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can for new service when power restored)
6194  {
6196  TrainController->StopTTClockMessage(90, HeadCode + ": A train without power can't form a new service");
6198  Utilities->CallLogPop(2145);
6199  return;
6200  }
6202  // finished all repeats
6203  {
6204  AnsiString NewHeadCode = ActionVectorEntryPtr->NonRepeatingShuttleLinkHeadCode;
6206  RepeatNumber = 0;
6207  UnplotTrain(6);
6210  StartSpeed = 0;
6212  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).TrainID = TrainID; // but note that RepeatNumber = 0
6213  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).RunningEntry = Running; // but note that RepeatNumber = 0
6215  HeadCode = NewHeadCode;
6216  StoppedAtLocation = true;
6217  PlotStartPosition(9);
6221  TerminatedMessageSent = false;
6222  Utilities->CallLogPop(1081);
6223  return;
6224  }
6225  int TempRepeatNumber = RepeatNumber + 1;
6226  // need the next repeat value in order to obtain a correct NewHeadCode, but don't increase it
6227  // until after LogAction or the wrong time will be used
6228  AnsiString NewHeadCode = TrainController->GetRepeatHeadCode(7, ActionVectorEntryPtr->OtherHeadCode, TempRepeatNumber, IncrementalDigits);
6229 
6231  RepeatNumber++;
6232  UnplotTrain(7);
6235  StartSpeed = 0;
6240  HeadCode = NewHeadCode;
6241  StoppedAtLocation = true;
6242  PlotStartPosition(8);
6244  // pale green
6247  TerminatedMessageSent = false;
6248  Utilities->CallLogPop(1082);
6249 }
6250 
6251 // ---------------------------------------------------------------------------
6252 
6254 {
6255  // Search ActionVector from the position after the entry value for Ptr to the end, and return true if find a Finish
6256  // entry before Fer or TimeLoc. No point checking for TimeTimeLoc since at a stop location now so a later TimeTimeLoc
6257  // must be preceded by a TimeLoc departure
6258  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsTrainTerminating" + "," + HeadCode);
6259  for(unsigned int x = 1; x < TrainDataEntryPtr->ActionVector.size(); x++)
6260  {
6262  {
6263  if(((ActionVectorEntryPtr + x)->Command == "Fer") || ((ActionVectorEntryPtr + x)->FormatType == TimeLoc))
6264  {
6265  Utilities->CallLogPop(1083);
6266  return false;
6267  }
6268  else if((ActionVectorEntryPtr + x)->SequenceType == Finish)
6269  {
6270  Utilities->CallLogPop(1084);
6271  return true;
6272  }
6273  }
6274  }
6275  Utilities->CallLogPop(1085);
6276  return false;
6277 }
6278 
6279 // ---------------------------------------------------------------------------
6280 
6281 bool TTrain::AbleToMove(int Caller)
6282 {
6283  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",AbleToMove" + "," + HeadCode);
6284  bool Able = true;
6285 
6286  if(Crashed || Derailed || StoppedAtBuffers || StoppedAtSignal || StoppedWithoutPower) // StoppedWithoutPower added at v2.4.0 &
6287  { // StoppedForTrainInFront removed as tested below
6288  Able = false;
6289  Utilities->CallLogPop(2146); // added v2.4.0
6290  return Able; // added v2.4.0
6291  }
6292  if(LeadElement > -1)
6293  {
6294  int FrontPos = Track->TrackElementAt(678, LeadElement).Conn[LeadExitPos];
6295  int FrontEntryPos = Track->TrackElementAt(679, LeadElement).ConnLinkPos[LeadExitPos];
6296  if((FrontPos > -1) && (TrainMode == Signaller) && StoppedForTrainInFront)
6297  {
6298  TTrackElement TrackElement = Track->TrackElementAt(680, FrontPos);
6299  if((TrackElement.TrackType != Bridge) && (TrackElement.TrainIDOnElement == -1))
6300  {
6301  Able = true;
6302  StoppedForTrainInFront = false;
6303  }
6304  else if((TrackElement.TrackType == Bridge) && (FrontEntryPos < 2) && (TrackElement.TrainIDOnBridgeTrackPos01 == -1))
6305  {
6306  Able = true;
6307  StoppedForTrainInFront = false;
6308  }
6309  else if((TrackElement.TrackType == Bridge) && (FrontEntryPos > 1) && (TrackElement.TrainIDOnBridgeTrackPos23 == -1))
6310  {
6311  Able = true;
6312  StoppedForTrainInFront = false;
6313  }
6314  }
6315  else
6316  {
6318  Able = false;
6319  // don't set StoppedAtBuffers as (presumably) StoppedAtLocation & leave it at that
6320  }
6321  }
6322  else // leaving at a continuation so keep going
6323  {
6324  Able = true;
6325  StoppedForTrainInFront = false;
6326  }
6327  Utilities->CallLogPop(1454);
6328  return Able;
6329 }
6330 
6331 // ---------------------------------------------------------------------------
6332 
6334 {
6335  // first check if a train immediately in front (may have moved there since this train stopped so StoppedForTrainInFront
6336  // won't be set; if there is a train then set StoppedForTrainInFront
6337  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",AbleToMoveButForSignal" + "," + HeadCode);
6338  // addition below for v1.3.2 after Carwyn Thomas fault reported 24/05/15 - need to check if exiting at continuation (LeadElement == -1) as if so fails at VecPos = .....
6339  if(LeadElement == -1) // exiting at continuation
6340  {
6341  Utilities->CallLogPop(2045);
6342  return false;
6343  }
6344  // end of addition
6345  int VecPos = Track->TrackElementAt(654, LeadElement).Conn[LeadExitPos];
6346  int NextEntryPos = Track->TrackElementAt(655, LeadElement).ConnLinkPos[LeadExitPos];
6347 
6348  if(Track->OtherTrainOnTrack(5, VecPos, NextEntryPos, TrainID))
6349  {
6350  StoppedForTrainInFront = true;
6351  Utilities->CallLogPop(1455);
6352  return false;
6353  }
6354  else
6355  {
6356  Utilities->CallLogPop(1456);
6358  // StoppedWithoutPower added v2.4.0
6359  }
6360 }
6361 
6362 // ---------------------------------------------------------------------------
6363 
6365 {
6366  // unplots & replots train, which checks for facing signal and sets StoppedAtSignal if req'd
6367  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SignallerChangeTrainDirection" + "," + HeadCode);
6368  TColor TempColour = BackgroundColour;
6369 
6370  UnplotTrain(8);
6373  StartSpeed = 0;
6374  PlotStartPosition(2);
6375  PlotTrainWithNewBackgroundColour(26, TempColour, Display);
6376 
6377  //now erase a stub route if there is one, added at v2.5.1
6378  //first element of route is now immediately behind the train (i.e. next to MidElement)
6379  if(MidEntryPos >= 0)
6380  {
6381  TTrackElement MidTrackElement = Track->TrackElementAt(1000, MidElement);
6382  int FirstRouteElementVecPos = MidTrackElement.Conn[MidEntryPos];
6383  int FirstRouteLinkPos = MidTrackElement.ConnLinkPos[MidEntryPos];
6384  int RouteNumber = -1;
6385  TAllRoutes::TRouteType RouteType = AllRoutes->GetRouteTypeAndNumber(35, FirstRouteElementVecPos, FirstRouteLinkPos, RouteNumber);
6386  if(RouteType == TAllRoutes::NotAutoSigsRoute)
6387  {
6388  TOneRoute &OR = AllRoutes->GetModifiableRouteAt(29, RouteNumber);
6389  TTrackElement TE = Track->TrackElementAt(1001, FirstRouteElementVecPos);
6390  if((TE.TrackType != SignalPost) && (TE.TrackType != Continuation))//all autosigs routes have signalpost or continuation at 0 so they are automatically excluded
6391  {
6392  while(OR.PrefDirSize() > 0) //remove the route up to but not including the next facing signal, in case a route extends to another signal
6393  {
6394  TPrefDirElement PDE = OR.GetFixedPrefDirElementAt(248, 0); //these will change at each element removal because OR is a reference to the real route
6395  int TVPos2 = PDE.GetTrackVectorPosition();
6396  TTrackElement TE2 = Track->TrackElementAt(1002, TVPos2);
6398  {
6399  AllRoutes->RemoveRouteElement(23, TE2.HLoc, TE2.VLoc, PDE.GetELink());
6400  }
6401  else
6402  {
6403  break;
6404  }
6405  }
6406  AllRoutes->RebuildRailwayFlag = true;
6407  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode, to replot without stub route
6408  }
6409  }
6410  }
6411  Utilities->CallLogPop(1102);
6412 }
6413 
6414 // ---------------------------------------------------------------------------
6415 
6417 {
6418  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TrainDataEntryPtr->ActionVector.front()) +
6419  ",FloatingLabelNextString" + "," + HeadCode);
6420  AnsiString RetStr = "", LocationName = "";
6421 
6422  if((Ptr->Command != "") && (Ptr->Command[1] == 'S'))
6423  {
6424  throw Exception("Error - start entry in FloatingLabelNextString");
6425  }
6426  if(Ptr->FormatType == TimeTimeLoc)
6427  {
6428  if(TrainMode == Timetable)
6429  {
6430  if(!TrainAtLocation(0, LocationName) || (LocationName != Ptr->LocationName))
6431  // not arrived yet in tt mode
6432  {
6433  RetStr = "Arrive " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(2, Ptr->ArrivalTime));
6434  }
6435  else
6436  {
6437  RetStr = "Depart " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(3, Ptr->DepartureTime));
6438  }
6439  }
6440  else // TrainMode == Signaller
6441  {
6442  if(!DepartureTimeSet) // not arrived yet
6443  {
6444  RetStr = "Arrive " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(45, Ptr->ArrivalTime));
6445  }
6446  else
6447  {
6448  RetStr = "Depart " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(36, Ptr->DepartureTime));
6449  }
6450  }
6451  }
6452  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
6453  {
6454  RetStr = "Arrive " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(4, Ptr->ArrivalTime));
6455  }
6456  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
6457  {
6458  RetStr = "Depart " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(5, Ptr->DepartureTime));
6459  }
6460  else if(Ptr->FormatType == PassTime) // new
6461  {
6462  RetStr = "Pass " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(31, Ptr->EventTime));
6463  }
6464  else if(Ptr->Command == "Fns")
6465  {
6466  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(8, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " +
6467  Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(6, Ptr->EventTime));
6468  RetStr = CheckNewServiceDepartureTime(0, Ptr, RepeatNumber, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
6469  }
6470  else if(Ptr->Command == "F-nshs")
6471  {
6472  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode + " at " + Ptr->LocationName + " at " +
6474  RetStr = CheckNewServiceDepartureTime(1, Ptr, 0, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
6475  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
6476  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
6477  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
6478  }
6479  else if((Ptr->Command == "Fns-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
6480  {
6481  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(9, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
6482  Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(7, Ptr->EventTime));
6483  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
6484  RetStr = CheckNewServiceDepartureTime(2, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
6485  }
6486  else if((Ptr->Command == "Fns-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
6487  {
6488  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode,
6489  + " at " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(8, Ptr->EventTime));
6490  RetStr = CheckNewServiceDepartureTime(3, Ptr, 0, Ptr->NonRepeatingShuttleLinkEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
6491  }
6492  else if((Ptr->Command == "Frh-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
6493  {
6494  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(10, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
6495  Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(9, Ptr->EventTime));
6496  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
6497  RetStr = CheckNewServiceDepartureTime(4, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
6498  }
6499  else if((Ptr->Command == "Frh-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
6500  {
6501  RetStr ="None, train terminated at " + Ptr->LocationName;
6502  }
6503  else if(Ptr->Command == "Frh")
6504  {
6505  RetStr = "None, train terminated at " + Ptr->LocationName;
6506  }
6507  else if(Ptr->Command == "Fer")
6508  {
6509  RetStr = "Exit railway" + TrainController->GetExitLocationAndAt(1, Ptr->ExitList) + " at " + Utilities->Format96HHMM(GetTrainTime(10, Ptr->EventTime));
6510  }
6511  else if(Ptr->Command == "Fjo")
6512  {
6513  RetStr = "Join " + TrainController->GetRepeatHeadCode(11, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName + " at " +
6515  }
6516  else if(Ptr->Command == "jbo")
6517  {
6518  RetStr = "Joined by " + TrainController->GetRepeatHeadCode(12, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
6519  " at " + Utilities->Format96HHMM(GetTrainTime(12, Ptr->EventTime));
6520  }
6521  else if(Ptr->Command == "fsp")
6522  {
6523  RetStr = "Front split to " + TrainController->GetRepeatHeadCode(13, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
6524  " at " + Utilities->Format96HHMM(GetTrainTime(13, Ptr->EventTime));
6525  }
6526  else if(Ptr->Command == "rsp")
6527  {
6528  RetStr = "Rear split to " + TrainController->GetRepeatHeadCode(14, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
6529  " at " + Utilities->Format96HHMM(GetTrainTime(14, Ptr->EventTime));
6530  }
6531  else if(Ptr->Command == "cdt")
6532  {
6533  RetStr = "Change direction at " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(15, Ptr->EventTime));
6534  }
6535  Utilities->CallLogPop(1124);
6536  return RetStr;
6537 }
6538 
6539 // ---------------------------------------------------------------------------
6540 
6541 AnsiString TTrain::CheckNewServiceDepartureTime(int Caller, TActionVectorEntry *Ptr, int RptNum, TTrainDataEntry *LinkedTrainDataPtr, AnsiString RetStr)
6542 {
6543  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TrainDataEntryPtr->ActionVector.front()) + ","
6544  + AnsiString(RptNum) + ",CheckNewServiceDepartureTime," + HeadCode);
6545  AnsiString DepTime = "", EventTime = "";
6546  bool CDTFlag = false; //reports if train changes direction before departs
6547  TActionVector NewServiceAV = LinkedTrainDataPtr->ActionVector;
6548  for(TActionVectorIterator AVI = NewServiceAV.begin(); AVI < NewServiceAV.end(); AVI++)
6549  {
6550  if(AVI->Command == "cdt")
6551  {
6552  CDTFlag = !CDTFlag; //toggles flag - allows for there being more than one cdt before departure
6553  continue;
6554  }
6555  if((AVI->Command == "fsp") || (AVI->Command == "rsp"))
6556  {
6557  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(19, AVI->EventTime, RptNum, IncrementalMinutes));
6558  RetStr += "\nNew service splits at " + EventTime;
6559  Utilities->CallLogPop(2234);
6560  return RetStr;
6561  }
6562  if(AVI->Command == "jbo")
6563  {
6564  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(20, AVI->EventTime, RptNum, IncrementalMinutes));
6565  RetStr += "\nNew service joined by " + AVI->OtherHeadCode + " at " + EventTime;
6566  Utilities->CallLogPop(2235);
6567  return RetStr;
6568  }
6569  if((AVI->FormatType == TimeLoc) && (AVI->DepartureTime > TDateTime(-1))) //departure time set
6570  {
6571  DepTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(17, AVI->DepartureTime, RptNum, IncrementalMinutes));
6572  if(CDTFlag)
6573  {
6574  RetStr += "\nNew service changes direction then departs at " + DepTime;
6575  }
6576  else
6577  {
6578  RetStr += "\nNew service departs at " + DepTime;
6579  }
6580  Utilities->CallLogPop(2236);
6581  return RetStr;
6582  }
6583  }
6584  Utilities->CallLogPop(2208);
6585  return RetStr; //if reach here then RetStr doesn't change
6586 }
6587 
6588 // ---------------------------------------------------------------------------
6589 
6591  // Enter with Ptr pointing to first action to be listed (i.e. next action)
6592 {
6593  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TrainDataEntryPtr->ActionVector.front()) +
6594  ",FloatingTimetableString" + "," + HeadCode);
6595  AnsiString RetStr = "", PartStr = "";
6596  int Count = 0;
6597 
6598  if((Ptr->Command != "") && (Ptr->Command[1] == 'S') && (TrainMode == Timetable))
6599  // can start in signaller control so exclude this
6600  {
6601  throw Exception("Error - start entry in FloatingTimetableString");
6602  }
6603  TActionVectorEntry *EntryPtr = Ptr; //used in TimeTime Loc check later
6604  bool FirstPass = true;
6605  Ptr--; // because incremented at start of loop
6606 
6607  // different first TimeTimeLoc display if in signaller control
6608  do
6609  {
6610  Ptr++;
6611  if((Ptr->FormatType == Repeat) || TimetableFinished)
6612  break;
6613  if((Ptr->FormatType == TimeTimeLoc) && FirstPass)
6614  {
6615  AnsiString TrainLoc = "";
6616  if(TrainMode == Timetable)
6617  {
6618  if(TrainAtLocation(1, TrainLoc) && (TrainLoc == Ptr->LocationName) && (Ptr == EntryPtr)) //added '&& (Ptr == EntryPtr)' at v2.6.0 when allow multiple same location entries
6619  {
6620  PartStr = Utilities->Format96HHMM(GetTrainTime(33, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
6621  }
6622  else if(Ptr->ArrivalTime == Ptr->DepartureTime)
6623  {
6624  PartStr = Utilities->Format96HHMM(GetTrainTime(34, Ptr->ArrivalTime)) + ": Arrive & depart from " + Ptr->LocationName;
6625  }
6626  else
6627  {
6628  PartStr = Utilities->Format96HHMM(GetTrainTime(16, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName + '\n' +
6629  Utilities->Format96HHMM(GetTrainTime(17, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
6630  Count++; // because there are 2 entries
6631  }
6632  }
6633  else // TrainMode == Signaller
6634  {
6635  if(DepartureTimeSet)
6636  {
6637  PartStr = Utilities->Format96HHMM(GetTrainTime(37, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
6638  }
6639  else if(Ptr->ArrivalTime == Ptr->DepartureTime)
6640  {
6641  PartStr = Utilities->Format96HHMM(GetTrainTime(38, Ptr->ArrivalTime)) + ": Arrive & depart from " + Ptr->LocationName;
6642  }
6643  else
6644  {
6645  PartStr = Utilities->Format96HHMM(GetTrainTime(39, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName + '\n' +
6646  Utilities->Format96HHMM(GetTrainTime(40, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
6647  Count++; // because there are 2 entries
6648  }
6649  }
6650  }
6651  else if((Ptr->FormatType == TimeTimeLoc) && !FirstPass)
6652  {
6653  AnsiString TrainLoc = "";
6654  if((TrainAtLocation(2, TrainLoc)) && (TrainLoc == Ptr->LocationName) && (Ptr == EntryPtr)) //added '&& (Ptr == EntryPtr)' at v2.6.0 when allow multiple same location entries
6655  {
6656  PartStr = Utilities->Format96HHMM(GetTrainTime(41, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
6657  }
6658  else if(Ptr->ArrivalTime == Ptr->DepartureTime)
6659  {
6660  PartStr = Utilities->Format96HHMM(GetTrainTime(42, Ptr->ArrivalTime)) + ": Arrive & depart from " + Ptr->LocationName;
6661  }
6662  else
6663  {
6664  PartStr = Utilities->Format96HHMM(GetTrainTime(43, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName + '\n' +
6665  Utilities->Format96HHMM(GetTrainTime(44, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
6666  Count++; // because there are 2 entries
6667  }
6668  }
6669  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
6670  {
6671  PartStr = Utilities->Format96HHMM(GetTrainTime(18, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName;
6672  }
6673  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
6674  {
6675  PartStr = Utilities->Format96HHMM(GetTrainTime(19, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
6676  }
6677  else if(Ptr->FormatType == PassTime) // new
6678  {
6679  PartStr = Utilities->Format96HHMM(GetTrainTime(30, Ptr->EventTime)) + ": Pass " + Ptr->LocationName;
6680  }
6681  else if(Ptr->Command == "Fns")
6682  {
6683  PartStr = Utilities->Format96HHMM(GetTrainTime(20, Ptr->EventTime)) + ": Form new service " + TrainController->GetRepeatHeadCode(15,
6684  Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
6685  PartStr = CheckNewServiceDepartureTime(5, Ptr, RepeatNumber, Ptr->LinkedTrainEntryPtr, PartStr); //if there is a next service this adds the new service departure time to PartStr
6686  }
6687  else if(Ptr->Command == "F-nshs")
6688  {
6689  PartStr = Utilities->Format96HHMM(GetTrainTime(35, Ptr->EventTime)) + ": Form new service " + Ptr->NonRepeatingShuttleLinkHeadCode + " at " +
6690  Ptr->LocationName;
6691  PartStr = CheckNewServiceDepartureTime(6, Ptr, 0, Ptr->LinkedTrainEntryPtr, PartStr); //if there is a next service this adds the new service departure time to RetStr
6692  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
6693  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
6694  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
6695  }
6696  else if((Ptr->Command == "Fns-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not the last repeat number
6697  {
6698  PartStr = Utilities->Format96HHMM(GetTrainTime(21, Ptr->EventTime)) + ": Form new service " + TrainController->GetRepeatHeadCode(16,
6699  Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " + Ptr->LocationName;
6700  // use RepeatNumber+1 because it's the repeat number of the NEXT shuttle service that is relevant
6701  PartStr = CheckNewServiceDepartureTime(7, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, PartStr); //if there is a next service this adds the new service departure time to RetStr
6702  }
6703  else if((Ptr->Command == "Fns-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
6704  {
6705  PartStr = Utilities->Format96HHMM(GetTrainTime(22, Ptr->EventTime)) + ": Form new service " + Ptr->NonRepeatingShuttleLinkHeadCode,
6706  + " at " + Ptr->LocationName;
6707  PartStr = CheckNewServiceDepartureTime(8, Ptr, 0, Ptr->NonRepeatingShuttleLinkEntryPtr, PartStr); //if there is a next service this adds the new service departure time to RetStr
6708  }
6709  else if((Ptr->Command == "Frh-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not the last repeat number
6710  {
6711  PartStr = Utilities->Format96HHMM(GetTrainTime(23, Ptr->EventTime)) + ": Form new service " + TrainController->GetRepeatHeadCode(17,
6712  Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " + Ptr->LocationName;
6713  // use RepeatNumber+1 because it's the repeat number of the NEXT shuttle service that is relevant
6714  PartStr = CheckNewServiceDepartureTime(9, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, PartStr); //if there is a next service this adds the new service departure time to RetStr
6715  }
6716  else if((Ptr->Command == "Frh-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
6717  {
6718  PartStr = "Terminate at " + Ptr->LocationName;
6719  }
6720  else if(Ptr->Command == "Frh")
6721  {
6722  PartStr = "Terminate at " + Ptr->LocationName;
6723  }
6724  else if(Ptr->Command == "Fer")
6725  {
6726  PartStr = Utilities->Format96HHMM(GetTrainTime(24, Ptr->EventTime)) + ": Exit railway" + TrainController->GetExitLocationAndAt(2, Ptr->ExitList);
6727  }
6728  else if(Ptr->Command == "Fjo")
6729  {
6730  PartStr = Utilities->Format96HHMM(GetTrainTime(25, Ptr->EventTime)) + ": Join " + TrainController->GetRepeatHeadCode(18, Ptr->OtherHeadCode,
6731  RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
6732  }
6733  else if(Ptr->Command == "jbo")
6734  {
6735  PartStr = Utilities->Format96HHMM(GetTrainTime(26, Ptr->EventTime)) + ": Joined by " + TrainController->GetRepeatHeadCode(19, Ptr->OtherHeadCode,
6736  RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
6737  }
6738  else if(Ptr->Command == "fsp")
6739  {
6740  PartStr = Utilities->Format96HHMM(GetTrainTime(27, Ptr->EventTime)) + ": Front split to " + TrainController->GetRepeatHeadCode(20,
6741  Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
6742  }
6743  else if(Ptr->Command == "rsp")
6744  {
6745  PartStr = Utilities->Format96HHMM(GetTrainTime(28, Ptr->EventTime)) + ": Rear split to " + TrainController->GetRepeatHeadCode(21,
6746  Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
6747  }
6748  else if(Ptr->Command == "cdt")
6749  {
6750  PartStr = Utilities->Format96HHMM(GetTrainTime(29, Ptr->EventTime)) + ": Change direction at " + Ptr->LocationName;
6751  }
6752  if(RetStr != "")
6753  RetStr = RetStr + '\n' + PartStr;
6754  else
6755  RetStr = PartStr;
6756  FirstPass = false;
6757  Count++;
6758  }
6759  while(!TimetableFinished && (Count < 32) && ((Ptr->Command == "") || ((Ptr->Command != "") && (Ptr->Command[1] != 'F'))));
6760  // limit of 32 allows a max of 34 entries (33 + 1 for the new service departure time) (may have gone from 32 to 34 because of a TimeTimeLoc), which with track and
6761  // train status gives a max of 48 lines, at 13 pixels each, = 624 pixels & screen height has 641 so will fit comfortably. Also 34 timetable entries is as far
6762  // forward as anyone should wish to see without looking at the full timetable
6763  if(TimetableFinished)
6764  {
6765  if(TrainMode == Timetable)
6766  RetStr = "Timetable finished";
6767  else
6768  RetStr = "No timetable";
6769  }
6770  Utilities->CallLogPop(1125);
6771  return RetStr;
6772 }
6773 
6774 // ---------------------------------------------------------------------------
6775 
6776 void TTrain::SaveOneSessionTrain(int Caller, std::ofstream &OutFile)
6777 {
6778  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveOneSessionTrain" + "," + HeadCode);
6779  Utilities->SaveFileString(OutFile, HeadCode);
6782  Utilities->SaveFileInt(OutFile, StartSpeed);
6785  Utilities->SaveFileInt(OutFile, RepeatNumber);
6788  Utilities->SaveFileInt(OutFile, Mass);
6791  Utilities->SaveFileDouble(OutFile, EntrySpeed);
6798  Utilities->SaveFileDouble(OutFile, BrakeRate);
6802  Utilities->SaveFileDouble(OutFile, double(EntryTime));
6803  Utilities->SaveFileDouble(OutFile, double(ExitTimeHalf));
6804  Utilities->SaveFileDouble(OutFile, double(ExitTimeFull));
6805  Utilities->SaveFileDouble(OutFile, double(ReleaseTime));
6806  Utilities->SaveFileDouble(OutFile, double(TRSTime));
6807  Utilities->SaveFileDouble(OutFile, double(LastActionTime));
6811  Utilities->SaveFileInt(OutFile, (short)TrainMode);
6816  Utilities->SaveFileBool(OutFile, Derailed);
6818  Utilities->SaveFileBool(OutFile, Crashed);
6825  Utilities->SaveFileBool(OutFile, NotInService);
6826  Utilities->SaveFileBool(OutFile, Plotted);
6827  Utilities->SaveFileBool(OutFile, TrainGone);
6828  Utilities->SaveFileBool(OutFile, SPADFlag);
6830  Utilities->SaveFileInt(OutFile, HOffset[0]);
6831  Utilities->SaveFileInt(OutFile, HOffset[1]);
6832  Utilities->SaveFileInt(OutFile, HOffset[2]);
6833  Utilities->SaveFileInt(OutFile, HOffset[3]);
6834  Utilities->SaveFileInt(OutFile, VOffset[0]);
6835  Utilities->SaveFileInt(OutFile, VOffset[1]);
6836  Utilities->SaveFileInt(OutFile, VOffset[2]);
6837  Utilities->SaveFileInt(OutFile, VOffset[3]);
6838  Utilities->SaveFileInt(OutFile, PlotElement[0]);
6839  Utilities->SaveFileInt(OutFile, PlotElement[1]);
6840  Utilities->SaveFileInt(OutFile, PlotElement[2]);
6841  Utilities->SaveFileInt(OutFile, PlotElement[3]);
6842  Utilities->SaveFileInt(OutFile, PlotEntryPos[0]);
6843  Utilities->SaveFileInt(OutFile, PlotEntryPos[1]);
6844  Utilities->SaveFileInt(OutFile, PlotEntryPos[2]);
6845  Utilities->SaveFileInt(OutFile, PlotEntryPos[3]);
6847  Utilities->SaveFileInt(OutFile, (short)Straddle);
6848  Utilities->SaveFileInt(OutFile, NextTrainID);
6849  Utilities->SaveFileInt(OutFile, TrainID);
6850  Utilities->SaveFileInt(OutFile, LeadElement);
6851  Utilities->SaveFileInt(OutFile, LeadEntryPos);
6852  Utilities->SaveFileInt(OutFile, LeadExitPos);
6853  Utilities->SaveFileInt(OutFile, MidElement);
6854  Utilities->SaveFileInt(OutFile, MidEntryPos);
6855  Utilities->SaveFileInt(OutFile, MidExitPos);
6856  Utilities->SaveFileInt(OutFile, LagElement);
6857  Utilities->SaveFileInt(OutFile, LagEntryPos);
6858  Utilities->SaveFileInt(OutFile, LagExitPos);
6859  int ColourNumber;
6860 
6862  ColourNumber = 0;
6864  ColourNumber = 1;
6866  ColourNumber = 2;
6868  ColourNumber = 3;
6870  ColourNumber = 4;
6872  ColourNumber = 5;
6874  ColourNumber = 6;
6876  ColourNumber = 7;
6878  ColourNumber = 8;
6880  ColourNumber = 9;
6882  ColourNumber = 10;
6884  ColourNumber = 11;
6886  ColourNumber = 12;
6887  else if(BackgroundColour == clTRSBackground)
6888  ColourNumber = 13;
6890  ColourNumber = 14; // added at v2.4.0
6891  Utilities->SaveFileInt(OutFile, ColourNumber);
6892 
6893  // additional data
6894  bool ForwardHeadCode;
6895 
6896  if(HeadCodePosition[3] == HeadCodeGrPtr[3])
6897  {
6898  ForwardHeadCode = true;
6899  }
6900  // can't use 'if(HeadCodePosition[0] == HeadCodeGrPtr[0])' as HeadCodePosition[0] is set to FrontCodePtr
6901  else
6902  {
6903  ForwardHeadCode = false;
6904  }
6905  Utilities->SaveFileBool(OutFile, ForwardHeadCode);
6906 
6907  int TrainDataEntryValue = TrainDataEntryPtr - &(TrainController->TrainDataVector.at(0));
6908 
6909  Utilities->SaveFileInt(OutFile, TrainDataEntryValue);
6910  int ActionVectorEntryValue = ActionVectorEntryPtr - &(TrainDataEntryPtr->ActionVector.at(0));
6911 
6912  Utilities->SaveFileInt(OutFile, ActionVectorEntryValue);
6913  // now the marker comes next which was ****** originally but used for RestoreTimetableLocation as well some time ago (came before the asterisks)
6914  // but at v2.4.0 need to include StoppedWithoutPower, while keeping length of marker at 6, because that is tested in earlier versions
6915  // so use the last asterisk position for this - 0 for false & 1 for true
6916  // note that failed train data is handled in InterfaceUnit.cpp & stored after the performance file
6917  AnsiString Marker;
6918 
6920  Marker = "*****1";
6921  else
6922  Marker = "*****0";
6923  if(RestoreTimetableLocation == "")
6924  Utilities->SaveFileString(OutFile, Marker);
6925  else
6926  {
6927  AnsiString CombinedString = RestoreTimetableLocation + Marker;
6928  Utilities->SaveFileString(OutFile, CombinedString);
6929  // RestoreTimetableLocation + marker
6930  }
6931  // Note: including RestoreTimetableLocation with the marker is to correct an oversight - it should have been saved earlier
6932  Utilities->CallLogPop(1457);
6933 }
6934 
6935 // ---------------------------------------------------------------------------
6936 
6937 void TTrain::LoadOneSessionTrain(int Caller, std::ifstream &InFile)
6938 {
6939  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadOneSessionTrain"); // don't have headcode yet
6940  HeadCode = Utilities->LoadFileString(InFile);
6943  StartSpeed = Utilities->LoadFileInt(InFile);
6945  if(SignallerMaxSpeed < 10)
6946  SignallerMaxSpeed = 10; // added at v0.6 to avoid low max speeds
6948  RepeatNumber = Utilities->LoadFileInt(InFile);
6951  Mass = Utilities->LoadFileInt(InFile);
6955  // above added at v2.1.0 for legacy session files where value may not have been limited
6957  EntrySpeed = Utilities->LoadFileDouble(InFile);
6961  if(TimetableMaxRunningSpeed < 10)
6962  {
6963  TimetableMaxRunningSpeed = 10; // added at v0.6 to avoid low max speeds
6964  }
6966  if(MaxRunningSpeed < 10)
6967  {
6968  MaxRunningSpeed = 10; // added at v0.6 to avoid low max speeds
6969  }
6972  BrakeRate = Utilities->LoadFileDouble(InFile);
6976  EntryTime = TDateTime(Utilities->LoadFileDouble(InFile));
6977  ExitTimeHalf = TDateTime(Utilities->LoadFileDouble(InFile));
6978  ExitTimeFull = TDateTime(Utilities->LoadFileDouble(InFile));
6979  ReleaseTime = TDateTime(Utilities->LoadFileDouble(InFile));
6980  TRSTime = TDateTime(Utilities->LoadFileDouble(InFile));
6981  LastActionTime = TDateTime(Utilities->LoadFileDouble(InFile));
6990  Derailed = Utilities->LoadFileBool(InFile);
6992  Crashed = Utilities->LoadFileBool(InFile);
6999  NotInService = Utilities->LoadFileBool(InFile);
7000  Plotted = Utilities->LoadFileBool(InFile);
7001  TrainGone = Utilities->LoadFileBool(InFile);
7002  SPADFlag = Utilities->LoadFileBool(InFile);
7004  HOffset[0] = Utilities->LoadFileInt(InFile);
7005  HOffset[1] = Utilities->LoadFileInt(InFile);
7006  HOffset[2] = Utilities->LoadFileInt(InFile);
7007  HOffset[3] = Utilities->LoadFileInt(InFile);
7008  VOffset[0] = Utilities->LoadFileInt(InFile);
7009  VOffset[1] = Utilities->LoadFileInt(InFile);
7010  VOffset[2] = Utilities->LoadFileInt(InFile);
7011  VOffset[3] = Utilities->LoadFileInt(InFile);
7012  PlotElement[0] = Utilities->LoadFileInt(InFile);
7013  PlotElement[1] = Utilities->LoadFileInt(InFile);
7014  PlotElement[2] = Utilities->LoadFileInt(InFile);
7015  PlotElement[3] = Utilities->LoadFileInt(InFile);
7016  PlotEntryPos[0] = Utilities->LoadFileInt(InFile);
7017  PlotEntryPos[1] = Utilities->LoadFileInt(InFile);
7018  PlotEntryPos[2] = Utilities->LoadFileInt(InFile);
7019  PlotEntryPos[3] = Utilities->LoadFileInt(InFile);
7021  Straddle = (TStraddle)(Utilities->LoadFileInt(InFile));
7022  NextTrainID = Utilities->LoadFileInt(InFile);
7023  // will be same for all but best to save all anyway
7024  TrainID = Utilities->LoadFileInt(InFile);
7025  LeadElement = Utilities->LoadFileInt(InFile);
7026  LeadEntryPos = Utilities->LoadFileInt(InFile);
7027  LeadExitPos = Utilities->LoadFileInt(InFile);
7028  MidElement = Utilities->LoadFileInt(InFile);
7029  MidEntryPos = Utilities->LoadFileInt(InFile);
7030  MidExitPos = Utilities->LoadFileInt(InFile);
7031  LagElement = Utilities->LoadFileInt(InFile);
7032  LagEntryPos = Utilities->LoadFileInt(InFile);
7033  LagExitPos = Utilities->LoadFileInt(InFile);
7034  int ColourNumber = TColor(Utilities->LoadFileInt(InFile));
7035 
7036  if(ColourNumber == 0)
7038  else if(ColourNumber == 1)
7040  else if(ColourNumber == 2)
7042  else if(ColourNumber == 3)
7044  else if(ColourNumber == 4)
7046  else if(ColourNumber == 5)
7048  else if(ColourNumber == 6)
7050  else if(ColourNumber == 7)
7052  else if(ColourNumber == 8)
7054  else if(ColourNumber == 9)
7056  else if(ColourNumber == 10)
7058  else if(ColourNumber == 11)
7060  else if(ColourNumber == 12)
7062  else if(ColourNumber == 13)
7064  else if(ColourNumber == 14)
7065  BackgroundColour = clTrainFailedBackground; // added at v2.4.0
7066 
7067  // additional data
7069  // sets the BackgroundColour to the loaded value
7070  bool ForwardHeadCode = Utilities->LoadFileBool(InFile);
7071 
7072  if(ForwardHeadCode)
7073  {
7074  for(int x = 0; x < 4; x++)
7075  {
7077  }
7078  }
7079  else
7080  {
7081  for(int x = 0; x < 4; x++)
7082  {
7083  HeadCodePosition[x] = HeadCodeGrPtr[3 - x];
7084  }
7085  }
7086 
7087  // if crashed & in timetable mode then change FrontCodePtr to black, if in signaller mode then change to blue whether crashed or not
7088  if(TrainMode == Timetable)
7089  {
7090  if(Crashed)
7092  else
7094  }
7095  else
7098  // pick up background bitmaps, none if MidLag as no train plotted - entering at continuation
7099  if(Straddle == LeadMid)
7100  {
7101  if(LeadElement > -1)
7103  if(LeadElement > -1)
7105  if(MidElement > -1)
7107  if(MidElement > -1)
7109  }
7110  else if(Straddle == LeadMidLag)
7111  {
7112  if(LeadElement > -1)
7114  if(MidElement > -1)
7116  if(MidElement > -1)
7118  if(LagElement > -1)
7120  }
7121 
7122  int TrainDataEntryValue = Utilities->LoadFileInt(InFile);
7123 
7124  TrainDataEntryPtr = &(TrainController->TrainDataVector.at(0)) + TrainDataEntryValue;
7125  int ActionVectorEntryValue = Utilities->LoadFileInt(InFile);
7126 
7127  ActionVectorEntryPtr = &(TrainDataEntryPtr->ActionVector.at(0)) + ActionVectorEntryValue;
7128 
7129  // need to set the TrainID if arriving at a continuation but hasn't been plotted yet
7130  if(LeadElement > -1)
7131  // need to include this in case train exiting & no lead element
7132  {
7134  {
7135  Track->TrackElementAt(668, LeadElement).TrainIDOnElement = TrainID; // no need to stop gap flashing if a continuation
7136  }
7137  }
7138  AValue = sqrt(2 * PowerAtRail / Mass);
7139 
7140  AnsiString LocationAndMarker = Utilities->LoadFileString(InFile);
7141 
7142  // possible RestoreTimetableLocation + Marker, where Marker is
7143  // "*****0" for !StoppedWithoutPower and "*****1" otherwise (from v2.4.0)
7144  // Note: including RestoreTimetableLocation with the marker is to correct an oversight - RestoreTimetableLocation should have been saved earlier
7145  // added at beta v0.2e
7146  if((LocationAndMarker[1] != '*') && (LocationAndMarker.Length() > 6))
7147  // name not allowed to include the '*' character
7148  {
7149  AnsiString Location = LocationAndMarker.SubString(1, LocationAndMarker.Length() - 6);
7150  bool GiveMessagesFalse = false;
7151  bool CheckLocationsExistInRailwayTrue = true;
7152  if(TrainController->CheckLocationValidity(3, Location, GiveMessagesFalse, CheckLocationsExistInRailwayTrue))
7153  { // otherwise take no action
7154  RestoreTimetableLocation = Location;
7155  }
7156  }
7157  AnsiString Marker = LocationAndMarker.SubString(LocationAndMarker.Length() - 5, 6);
7158 
7159  StoppedWithoutPower = false;
7160  if(Marker[6] == '1')
7161  {
7162  StoppedWithoutPower = true;
7163  }
7164  Utilities->CallLogPop(1458);
7165 }
7166 
7167 // ---------------------------------------------------------------------------
7168 
7169 bool TTrain::CheckOneSessionTrain(std::ifstream &InFile)
7170 {
7172  return false; // HeadCode
7173 
7174  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
7175  return false; // RearStartElement
7176 
7177  if(!Utilities->CheckFileInt(InFile, 0, 3))
7178  return false; // RearStartExitPos
7179 
7180  if(!Utilities->CheckFileInt(InFile, 0, MaximumSpeedLimit))
7181  return false; // StartSpeed
7182 
7183  if(!Utilities->CheckFileInt(InFile, 0, MaximumSpeedLimit))
7184  return false; // SignallerMaxSpeed
7185 
7186  if(!Utilities->CheckFileBool(InFile))
7187  return false; // HoldAtLocationInTTMode
7188 
7189  if(!Utilities->CheckFileInt(InFile, 0, 5760))
7190  return false; // RepeatNumber (max 96 x 60 at 1 min intervals)
7191 
7192  if(!Utilities->CheckFileInt(InFile, 0, 5760))
7193  return false; // IncrementalMinutes (max 96 x 60)
7194 
7195  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
7196  return false; // IncrementalDigits
7197 
7198  if(!Utilities->CheckFileInt(InFile, 0, 10000000))
7199  return false; // Mass
7200 
7201  if(!Utilities->CheckFileInt(InFile, 0, 100000000))
7202  return false;
7203 
7204  // FrontElementSpeedLimit - changed at v2.1.0 - effectively
7205  // not checked so as to allow for legacy session files, for new session files limit is set when loaded, see above
7206  if(!Utilities->CheckFileInt(InFile, 0, 10000000))
7207  return false; // FrontElementLength
7208 
7209  if(!Utilities->CheckFileDouble(InFile))
7210  return false; // EntrySpeed
7211 
7212  if(!Utilities->CheckFileDouble(InFile))
7213  return false; // ExitSpeedHalf
7214 
7215  if(!Utilities->CheckFileDouble(InFile))
7216  return false; // ExitSpeedFull
7217 
7218  if(!Utilities->CheckFileDouble(InFile))
7219  return false; // TimetableMaxRunningSpeed
7220 
7221  if(!Utilities->CheckFileDouble(InFile))
7222  return false; // MaxRunningSpeed
7223 
7224  if(!Utilities->CheckFileDouble(InFile))
7225  return false; // MaxExitSpeed
7226 
7227  if(!Utilities->CheckFileDouble(InFile))
7228  return false; // MaxBrakeRate
7229 
7230  if(!Utilities->CheckFileDouble(InFile))
7231  return false; // BrakeRate
7232 
7233  if(!Utilities->CheckFileDouble(InFile))
7234  return false; // PowerAtRail
7235 
7236  if(!Utilities->CheckFileBool(InFile))
7237  return false; // FirstHalfMove
7238 
7239  if(!Utilities->CheckFileBool(InFile))
7240  return false; // OneLengthAccelDecel
7241 
7242  if(!Utilities->CheckFileDouble(InFile))
7243  return false; // double(EntryTime)
7244 
7245  if(!Utilities->CheckFileDouble(InFile))
7246  return false; // double(ExitTimeHalf)
7247 
7248  if(!Utilities->CheckFileDouble(InFile))
7249  return false; // double(ExitTimeFull)
7250 
7251  if(!Utilities->CheckFileDouble(InFile))
7252  return false; // double(ReleaseTime)
7253 
7254  if(!Utilities->CheckFileDouble(InFile))
7255  return false; // double(TRSTime)
7256 
7257  if(!Utilities->CheckFileDouble(InFile))
7258  return false; // double(LastActionTime)
7259 
7260  if(!Utilities->CheckFileBool(InFile))
7261  return false; // CallingOnFlag
7262 
7263  if(!Utilities->CheckFileBool(InFile))
7264  return false; // BeingCalledOn
7265 
7266  if(!Utilities->CheckFileBool(InFile))
7267  return false; // DepartureTimeSet
7268 
7269  if(!Utilities->CheckFileInt(InFile, 0, 2))
7270  return false; // (short)TrainMode
7271 
7272  if(!Utilities->CheckFileBool(InFile))
7273  return false; // TimetableFinished
7274 
7275  if(!Utilities->CheckFileBool(InFile))
7276  return false; // LastActionDelayFlag
7277 
7278  if(!Utilities->CheckFileBool(InFile))
7279  return false; // SignallerRemoved
7280 
7281  if(!Utilities->CheckFileBool(InFile))
7282  return false; // TerminatedMessageSent
7283 
7284  if(!Utilities->CheckFileBool(InFile))
7285  return false; // Derailed
7286 
7287  if(!Utilities->CheckFileBool(InFile))
7288  return false; // DerailPending
7289 
7290  if(!Utilities->CheckFileBool(InFile))
7291  return false; // Crashed
7292 
7293  if(!Utilities->CheckFileBool(InFile))
7294  return false; // StoppedAtBuffers
7295 
7296  if(!Utilities->CheckFileBool(InFile))
7297  return false; // StoppedAtSignal
7298 
7299  if(!Utilities->CheckFileBool(InFile))
7300  return false; // StoppedAtLocation
7301 
7302  if(!Utilities->CheckFileBool(InFile))
7303  return false; // SignallerStopped
7304 
7305  if(!Utilities->CheckFileBool(InFile))
7306  return false; // StoppedAfterSPAD
7307 
7308  if(!Utilities->CheckFileBool(InFile))
7309  return false; // StoppedForTrainInFront
7310 
7311  if(!Utilities->CheckFileBool(InFile))
7312  return false; // NotInService
7313 
7314  if(!Utilities->CheckFileBool(InFile))
7315  return false; // Plotted
7316 
7317  if(!Utilities->CheckFileBool(InFile))
7318  return false; // TrainGone
7319 
7320  if(!Utilities->CheckFileBool(InFile))
7321  return false; // SPADFlag
7322 
7323  if(!Utilities->CheckFileBool(InFile))
7324  return false; // TimeTimeLocArrived
7325 
7326  if(!Utilities->CheckFileInt(InFile, 0, 15))
7327  return false; // HOffset[0]
7328 
7329  if(!Utilities->CheckFileInt(InFile, 0, 15))
7330  return false; // HOffset[1]
7331 
7332  if(!Utilities->CheckFileInt(InFile, 0, 15))
7333  return false; // HOffset[2]
7334 
7335  if(!Utilities->CheckFileInt(InFile, 0, 15))
7336  return false; // HOffset[3]
7337 
7338  if(!Utilities->CheckFileInt(InFile, 0, 15))
7339  return false; // VOffset[0]
7340 
7341  if(!Utilities->CheckFileInt(InFile, 0, 15))
7342  return false; // VOffset[1]
7343 
7344  if(!Utilities->CheckFileInt(InFile, 0, 15))
7345  return false; // VOffset[2]
7346 
7347  if(!Utilities->CheckFileInt(InFile, 0, 15))
7348  return false; // VOffset[3]
7349 
7350  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
7351  return false; // PlotElement[0]
7352 
7353  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
7354  return false; // PlotElement[1]
7355 
7356  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
7357  return false; // PlotElement[2]
7358 
7359  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
7360  return false; // PlotElement[3]
7361 
7362  if(!Utilities->CheckFileInt(InFile, 0, 3))
7363  return false; // PlotEntryPos[0]
7364 
7365  if(!Utilities->CheckFileInt(InFile, 0, 3))
7366  return false; // PlotEntryPos[1]
7367 
7368  if(!Utilities->CheckFileInt(InFile, 0, 3))
7369  return false; // PlotEntryPos[2]
7370 
7371  if(!Utilities->CheckFileInt(InFile, 0, 3))
7372  return false; // PlotEntryPos[3]
7373 
7374  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
7375  return false; // TrainCrashedInto
7376 
7377  if(!Utilities->CheckFileInt(InFile, 0, 2))
7378  return false; // (short)Straddle
7379 
7380  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
7381  return false; // NextTrainID
7382 
7383  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
7384  return false; // TrainID
7385 
7386  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
7387  return false; // LeadElement
7388 
7389  if(!Utilities->CheckFileInt(InFile, 0, 3))
7390  return false; // LeadEntryPos
7391 
7392  if(!Utilities->CheckFileInt(InFile, 0, 3))
7393  return false; // LeadExitPos
7394 
7395  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
7396  return false; // MidElement
7397 
7398  if(!Utilities->CheckFileInt(InFile, 0, 3))
7399  return false; // MidEntryPos
7400 
7401  if(!Utilities->CheckFileInt(InFile, 0, 3))
7402  return false; // MidExitPos
7403 
7404  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
7405  return false; // LagElement
7406 
7407  if(!Utilities->CheckFileInt(InFile, 0, 3))
7408  return false; // LagEntryPos
7409 
7410  if(!Utilities->CheckFileInt(InFile, 0, 3))
7411  return false; // LagExitPos
7412 
7413  if(!Utilities->CheckFileInt(InFile, 0, 14))
7414  return false;
7415 
7416  // Background colour number //14 is failed colour at v2.4.0
7417  if(!Utilities->CheckFileBool(InFile))
7418  return false; // ForwardHeadCode
7419 
7420  if(!Utilities->CheckFileInt(InFile, 0, 10000))
7421  return false; // TrainDataEntryValue
7422 
7423  if(!Utilities->CheckFileInt(InFile, 0, 10000))
7424  return false; // ActionVectorEntryValue
7425 
7427  return false; // End of train marker + possible RestoreTimetableLocation
7428 
7429  // and StoppedWithoutPower flag
7430  return true;
7431 }
7432 
7433 // ---------------------------------------------------------------------------
7434 
7435 void TTrain::PlotTrainInZoomOutMode(int Caller, bool Flash)
7436 {
7437  // order below reflects significance so earlier shows first, as may have more than one flag set
7438  // only plot flashing trains when Flash is true
7439 
7440 /*
7441  clCrashedBackground (TColor)0x0000FF red
7442  clDerailedBackground (TColor)0x0000FF red
7443  clSPADBackground (TColor)0x00FFFF yellow
7444  clTrainFailedBackground (TColor)0x0066FF orange new at v2.4.0
7445  clCallOnBackground (TColor)0xFF33FF light magenta
7446  clSignalStopBackground (TColor)0x00FF66 green
7447  clBufferAttentionNeeded (TColor)0xFFFF00 cyan
7448  clStationStopBackground (TColor)0xCCFFCC pale green
7449  clTRSBackground (TColor)0xFFCCFF light pink
7450  clBufferStopBackground (TColor)0xFFFFCC pale cyan
7451  clStoppedTrainInFront (TColor)0xFF9999 lavender blue
7452  clSignallerStopped (TColor)0x99CCFF caramel
7453  clNormalBackground (TColor)0xCCCCCC grey
7454 */
7455 
7456  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainInZoomOutMode" + "," + HeadCode);
7457  bool HideFlashingTrain = true;
7458  // hide it when Flash false so it blinks on and off
7459  // if don't hide it it stays displayed all the time
7460  Graphics::TBitmap *SmallTrainBitmap;
7461 
7462  // NB ensure retain same order as zoomed in order so colours correspond
7464  {
7465  TrainController->CrashWarning = true;
7466  SmallTrainBitmap = RailGraphics->smRed;
7467  }
7469  {
7471  SmallTrainBitmap = RailGraphics->smRed;
7472  }
7474  {
7475  TrainController->SPADWarning = true;
7476  SmallTrainBitmap = RailGraphics->smYellow;
7477  }
7479  {
7481  SmallTrainBitmap = RailGraphics->smOrange;
7482  }
7484  {
7486  SmallTrainBitmap = RailGraphics->smMagenta;
7487  }
7489  {
7491  SmallTrainBitmap = RailGraphics->smBrightGreen;
7492  }
7494  {
7496  SmallTrainBitmap = RailGraphics->smCyan;
7497  }
7499  {
7500  SmallTrainBitmap = RailGraphics->smPaleGreen;
7501  HideFlashingTrain = false;
7502  }
7504  {
7505  SmallTrainBitmap = RailGraphics->smCyan;
7506  HideFlashingTrain = false;
7507  }
7509  {
7510  SmallTrainBitmap = RailGraphics->smLightBlue;
7511  HideFlashingTrain = false;
7512  }
7514  {
7515  SmallTrainBitmap = RailGraphics->smCaramel;
7516  HideFlashingTrain = false;
7517  }
7518  else
7519  {
7520  SmallTrainBitmap = RailGraphics->smBlack; // moving
7521  HideFlashingTrain = false;
7522  }
7523 
7524  // now plot the new train
7525  // just plot lead & mid, unless lead == -1 in which case plot mid & lag
7526  if((LeadElement > -1) && (!HideFlashingTrain || Flash))
7527  {
7528  Display->PlotSmallOutput(4, Track->TrackElementAt(441, LeadElement).HLoc * 4, Track->TrackElementAt(442, LeadElement).VLoc * 4, SmallTrainBitmap);
7529  }
7530  if((MidElement > -1) && (!HideFlashingTrain || Flash))
7531  {
7532  Display->PlotSmallOutput(5, Track->TrackElementAt(443, MidElement).HLoc * 4, Track->TrackElementAt(444, MidElement).VLoc * 4, SmallTrainBitmap);
7533  }
7534  if((LeadElement == -1) && (LagElement > -1) && (!HideFlashingTrain || Flash))
7535  {
7536  Display->PlotSmallOutput(6, Track->TrackElementAt(445, LagElement).HLoc * 4, Track->TrackElementAt(446, LagElement).VLoc * 4, SmallTrainBitmap);
7537  }
7541  Utilities->CallLogPop(1459);
7542 }
7543 
7544 // ---------------------------------------------------------------------------
7545 
7547 {
7548  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UnplotTrainInZoomOutMode," + AnsiString(TrainID) + "," + HeadCode);
7549  if(!Display->ZoomOutFlag)
7550  {
7551  Utilities->CallLogPop(1304);
7552  return;
7553  }
7554  for(int y = 0; y < 3; y++)
7555  {
7556  if(OldZoomOutElement[y] > -1)
7557  {
7558  bool FoundFlag = false;
7559  TTrackElement ATElement = Track->TrackElementAt(717, OldZoomOutElement[y]);
7560  TTrackElement IATElement1, IATElement2;
7561  // default elements to begin with
7562  Display->PlotSmallOutput(7, ATElement.HLoc * 4, ATElement.VLoc * 4, RailGraphics->smSolidBgnd); // plot the blank
7563  TTrack::TIMPair IMPair = Track->GetVectorPositionsFromInactiveTrackMap(14, ATElement.HLoc, ATElement.VLoc, FoundFlag);
7564  // Note, have to plot inactives before track because track has to overwrite NamedLocationElements
7565  if(FoundFlag)
7566  {
7567  IATElement1 = Track->InactiveTrackElementAt(87, IMPair.first);
7568  Display->PlotSmallOutput(8, IATElement1.HLoc * 4, IATElement1.VLoc * 4, IATElement1.SmallGraphicPtr);
7569  if(IMPair.first != IMPair.second)
7570  {
7571  IATElement2 = Track->InactiveTrackElementAt(88, IMPair.second);
7572  Display->PlotSmallOutput(9, IATElement2.HLoc * 4, IATElement2.VLoc * 4, IATElement2.SmallGraphicPtr);
7573  }
7574  }
7575  Display->PlotSmallOutput(10, ATElement.HLoc * 4, ATElement.VLoc * 4, ATElement.SmallGraphicPtr);
7576  }
7577  }
7578  Utilities->CallLogPop(1305);
7579 }
7580 
7581 // ---------------------------------------------------------------------------
7582 
7583 bool TTrain::TrainAtLocation(int Caller, AnsiString &LocationName)
7584 {
7585  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainAtLocation" + "," + HeadCode);
7586  LocationName = "";
7587  if(!StoppedAtLocation)
7588  {
7589  Utilities->CallLogPop(1398);
7590  return false;
7591  }
7592  if(LeadElement > -1)
7593  {
7595  }
7596  if((LocationName == "") && (MidElement > -1))
7597  {
7598  LocationName = Track->TrackElementAt(682, MidElement).ActiveTrackElementName;
7599  }
7600  if((LocationName == "") && (LagElement > -1))
7601  {
7602  LocationName = Track->TrackElementAt(683, LagElement).ActiveTrackElementName;
7603  }
7604  if(LocationName == "")
7605  {
7606  throw Exception("Error - Location name not set in TrainAtLocation");
7607  }
7608  Utilities->CallLogPop(1399);
7609  return true;
7610 }
7611 
7612 // ---------------------------------------------------------------------------
7613 
7614 void TTrain::PlotTrain(int Caller, TDisplay *Disp)
7615 {
7616  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrain" + "," + HeadCode);
7617  for(int x = 0; x < 4; x++)
7618  {
7619  PlotTrainGraphic(7, x, Disp);
7620  }
7621  Utilities->CallLogPop(647);
7622 }
7623 
7624 // ---------------------------------------------------------------------------
7625 
7626 void TTrain::WriteTrainToImage(int Caller, Graphics::TBitmap *Bitmap)
7627 {
7628  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",WriteTrainToImage" + "," + HeadCode);
7629  for(int x = 0; x < 4; x++)
7630  {
7631  if(PlotElement[x] > -1)
7632  {
7633  Bitmap->Canvas->Draw(((Track->TrackElementAt(744, PlotElement[x]).HLoc - Track->GetHLocMin()) * 16 + HOffset[x]),
7634  ((Track->TrackElementAt(745, PlotElement[x]).VLoc - Track->GetVLocMin()) * 16 + VOffset[x]), HeadCodePosition[x]);
7635  }
7636  }
7637  Utilities->CallLogPop(1708);
7638 }
7639 
7640 // ---------------------------------------------------------------------------
7641 
7642 bool TTrain::LinkOccupied(int Caller, int TrackVectorPosition, int LinkNumber) // added at v1.2.0
7643 {
7644  // return true for any part of train occupying LinkNumber at TrackVectorPosition, false for anything else, including no LinkNumber & no TrackVectorPosition
7645  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LinkOccupied," + AnsiString(TrackVectorPosition) + "," +
7646  AnsiString(LinkNumber) + "," + HeadCode);
7647 
7648 /* Note on Straddle: Straddle defines the actual train position wrt Lag, Mid & Lead elements at all times other than within UpdateTrain. Is only MidLag outside UpdateTrain
7649  on first entry at a continuation (with no train plotted), and that has no relevance here. In all other cases it is either LeadMid (when train fully
7650  on Lead & Mid elements) or LeadMidLag (when train straddling 3 elements).
7651 */
7652 
7653  // note that MidElement always fully occupied
7654  if((MidElement == TrackVectorPosition) && ((Track->TrackElementAt(883, TrackVectorPosition).Link[MidEntryPos] == LinkNumber) || (Track->TrackElementAt(884,
7655  TrackVectorPosition).Link[MidExitPos] == LinkNumber)))
7656  {
7657  Utilities->CallLogPop(2005);
7658  return true;
7659  }
7660  if(Straddle == LeadMid)
7661  {
7662  if((LeadElement == TrackVectorPosition) && ((Track->TrackElementAt(885, TrackVectorPosition).Link[LeadEntryPos] == LinkNumber) ||
7663  (Track->TrackElementAt(886, TrackVectorPosition).Link[LeadExitPos] == LinkNumber)))
7664  {
7665  Utilities->CallLogPop(2006);
7666  return true;
7667  }
7668  }
7669  else if(Straddle == LeadMidLag)
7670  {
7671  if((LeadElement == TrackVectorPosition) && (Track->TrackElementAt(887, TrackVectorPosition).Link[LeadEntryPos] == LinkNumber))
7672  // only interested in LeadEntryPos as train not occupying ExitPos yet
7673  {
7674  Utilities->CallLogPop(2007);
7675  return true;
7676  }
7677  else if((LagElement == TrackVectorPosition) && (Track->TrackElementAt(888, TrackVectorPosition).Link[LagExitPos] == LinkNumber))
7678  // only interested in LagExitPos as train has left EntryPos
7679  {
7680  Utilities->CallLogPop(2008);
7681  return true;
7682  }
7683  }
7684  Utilities->CallLogPop(2009);
7685  return false;
7686 }
7687 
7688 // ---------------------------------------------------------------------------
7689 
7690 float TTrain::CalcTimeToAct(int Caller) // only called for running trains
7695 {
7696  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CalcTimeToAct, " + HeadCode);
7697  int DistanceToRedSignal = 0;
7698  float TimeToAct = 0;
7699  float MinsEarly = 0; //added at v2.6.1
7700  TDateTime DepartureTime; //added at v2.6.1
7701 
7702  if(TrainFailed)
7703  {
7704  Utilities->CallLogPop(2147);
7705  return 0; // time to act now
7706  }
7707 
7708  if(SignallerStopped)
7709  {
7710  Utilities->CallLogPop(2080);
7711  return -1;
7712  }
7713 
7714  if(!Stopped() || StoppedAtLocation)
7715  {
7716  // calc distance to next red signal but check for continuation exit
7717  if(LeadElement == -1)
7718  // if -1 it's on an end element so no action needed
7719  {
7720  Utilities->CallLogPop(2075);
7721  return -1;
7722  }
7723  else
7724  {
7725  int FirstPosToBeMeasured = Track->TrackElementAt(953, LeadElement).Conn[LeadExitPos];
7726  int FirstEntryPos = Track->TrackElementAt(954, LeadElement).ConnLinkPos[LeadExitPos];
7727  if((Straddle == LeadMidLag) && (TrainMode == Timetable))
7728 /* In TTMode it's important to set the first element to be measured ahead of the lead element only when the train fully on
7729  2 elements. Otherwise, if the train is only half on the lead element and approaching a station stop where the platform doesn't
7730  extend beyond the lead element stop point, the element ahead of the lead element is not a location whereas the ActionVector
7731  still points to the station stop location. In these circumstances the train hasn't yet stopped, so the dwell time at the
7732  stop isn't calculated, and the station to be stopped at isn't found as a future stop and nor are any other future stops
7733  because the ActionVector name never matches a future station. Hence all dwell times are omitted until the train lands fully
7734  on two elements. To avoid this when Straddle is LeadMidLag the first element to be measured is set to the lead element, so
7735  before the train has stopped the current station is still recognised as a future stop.
7736  In signaller mode stops don't count, and if pass red signal command is given then when have LeadMidLag the current element
7737  becomes the signal, and the time to act indication becomes 'NOW'.
7738 */
7739  {
7740  FirstPosToBeMeasured = LeadElement;
7741  FirstEntryPos = LeadEntryPos;
7742  }
7743  float CurrentStopTime; // set to 0 at start of function
7744  float LaterStopTime; // set to 0 at start of function
7745  float RecoverableTime; // set to 0 at start of function
7746  int AvTrackSpeed; // set to zero at start of function
7747  bool SigControlAndCanPassRedSignal = ((TrainMode == Signaller) && AllowedToPassRedSignal);
7748  DistanceToRedSignal = TrainController->CalcDistanceToRedSignalandStopTime(0, FirstPosToBeMeasured, FirstEntryPos, SigControlAndCanPassRedSignal,
7749  ActionVectorEntryPtr, HeadCode, TrainID, CurrentStopTime, LaterStopTime, RecoverableTime, AvTrackSpeed);
7750  if(DistanceToRedSignal == -1) // -1 for no action needed
7751  {
7752  Utilities->CallLogPop(2076);
7753  return -1;
7754  }
7755 /* Have MinsDelayed; pos or neg,
7756  CurrentStopTime; pos or zero
7757  LaterStopTime; pos or zero
7758  RecoverableTime; pos or zero
7759 
7760  & from these calculate TotalStopTime. noting that:
7761  If stopped CurrentStopTime automatically adjusts for all early running and for as much late running as possible
7762  RecoverableTime always < LaterStopTime or both zero
7763  can't subtract more than RecoverableTime (MinsDelayed > 0)
7764  only subtract from LaterStopTime, not CurrentTime (MinsDelayed > 0)
7765  only subtract from LaterStopTime if LaterStopTime > 0 (MinsDelayed > 0)
7766  only add to LaterStopTime if LaterStopTime > 0 (MinsDelayed < 0)
7767  if running early & stopped at location CurrentStopTime will automatically include the excess
7768 */
7769  float TimeToSubtract, TotalStopTime;
7770  if(MinsDelayed > RecoverableTime)
7771  TimeToSubtract = RecoverableTime;
7772  else
7773  TimeToSubtract = MinsDelayed; // may be negative;
7774 
7775  if((AvTrackSpeed > 0) && (DistanceToStationStop < DistanceToRedSignal) && (DistanceToStationStop > 0))//protection against div by zero, not needed of no stop before red signal, DistanceToStationStop != 0 as set to 0 if invalid
7776  {//added at v2.6.1, DistanceToStationStop is calculated in SetTrainMovementValues, AvTrackSpeed is average to next red signal, but should be ok to use for next station stop
7777  //first find departure time from the next stop
7779  {
7780  DepartureTime = ActionVectorEntryPtr->DepartureTime;
7781  }
7782  else if((ActionVectorEntryPtr->FormatType == TimeLoc) && (ActionVectorEntryPtr->ArrivalTime != TDateTime(-1))) // must be an arrival
7783  {
7784  if((ActionVectorEntryPtr + 1)->FormatType == TimeLoc)
7785  // must be a departure
7786  DepartureTime = (ActionVectorEntryPtr + 1)->DepartureTime;
7787  }
7788  MinsEarly = (double(DepartureTime - TrainController->TTClockTime) * 86400 / 60) - (DistanceToStationStop * 3.6 / 60 / AvTrackSpeed);
7789  if(MinsEarly < 0)
7790  {
7791  MinsEarly = 0;
7792  }
7793  }
7794 
7795  if(MinsDelayed < 0) // MinsDelayed < 0 means have arrived early at a station
7796  {
7797  if(CurrentStopTime > 0)
7798  TotalStopTime = CurrentStopTime + LaterStopTime;
7799  // stopped at loc, will depart on time
7800  else
7801  TotalStopTime = LaterStopTime - MinsDelayed;
7802  // not stopped, will depart on time at first later stop so add the delay
7803  }
7804  else if((MinsEarly > 0) && !Stopped()) //running early
7805  {
7806  TotalStopTime = LaterStopTime + MinsEarly;
7807  }
7808  else // on time or running late
7809  {
7810  if(LaterStopTime == 0)
7811  TotalStopTime = CurrentStopTime;
7812  // no later stops, if stopped now will depart as soon as possible,
7813  // if not stopped no stop times to add
7814  else
7815  TotalStopTime = CurrentStopTime + LaterStopTime - TimeToSubtract; // later stops so deduct as much as can
7816  }
7817 
7818  if(AvTrackSpeed < 30)
7819  AvTrackSpeed = 30;
7820  int Speed = AvTrackSpeed;
7821  if(AvTrackSpeed > int(MaxRunningSpeed))
7822  Speed = int(MaxRunningSpeed);
7823  if(TrainMode == Signaller)
7824  {
7825  Speed = SignallerMaxSpeed;
7826  TotalStopTime = 0;
7827  }
7828  TimeToAct = TotalStopTime + DistanceToRedSignal * 3.6 / 60 / Speed;
7829  // accel & decel taken into account in
7830  // CalcDistanceToRedSignalandStopTime
7831  // 3.6 convertsKm/h to m/s & 60 converts seconds to minutes
7832  Utilities->CallLogPop(2079);
7833  return TimeToAct;
7834  }
7835  }
7836  else // stopped not at location
7837  {
7839  TimeToAct = 0.0;
7840  // but if stopped at a signal & autosigs route after it then ok
7841  if(StoppedAtSignal)
7842  {
7843  int NextElement = Track->TrackElementAt(928, LeadElement).Conn[LeadExitPos];
7844  int NextEntryPos = Track->TrackElementAt(929, LeadElement).ConnLinkPos[LeadExitPos];
7845  int NextExitPos;
7846  if(Track->TrackElementAt(930, NextElement).TrackType == Points)
7847  {
7848  if((NextEntryPos == 0) || (NextEntryPos == 2))
7849  // leading entry point
7850  {
7851  if(Track->TrackElementAt(931, NextElement).Attribute == 0)
7852  NextExitPos = 1;
7853  else
7854  NextExitPos = 3;
7855  }
7856  else
7857  NextExitPos = 0; // trailing entry point
7858  }
7859  else
7860  NextExitPos = Track->GetNonPointsOppositeLinkPos(NextEntryPos);
7861  int NextButOneElement = Track->TrackElementAt(932, NextElement).Conn[NextExitPos];
7862  int NextButOneEntryPos = Track->TrackElementAt(933, NextElement).ConnLinkPos[NextExitPos];
7863  int RouteNumber; // holder for referenced value, not used
7864  if(AllRoutes->GetRouteTypeAndNumber(32, NextButOneElement, NextButOneEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
7865  {
7866  TimeToAct = -1;
7867  }
7868  }
7869  Utilities->CallLogPop(2074);
7870  return TimeToAct;
7871  }
7872 }
7873 
7874 // ---------------------------------------------------------------------------
7875 
7877 {
7878  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainOnContinuation, " + HeadCode);
7879  if(LeadElement > -1)
7880  {
7882  {
7883  Utilities->CallLogPop(2148);
7884  return true;
7885  }
7886  }
7887  if(MidElement > -1)
7888  {
7890  {
7891  Utilities->CallLogPop(2149);
7892  return true;
7893  }
7894  }
7895  if(LagElement > -1)
7896  {
7898  {
7899  Utilities->CallLogPop(2150);
7900  return true;
7901  }
7902  }
7903  Utilities->CallLogPop(2151);
7904  return false;
7905 }
7906 
7907 // ---------------------------------------------------------------------------
7908 // TTrainController
7909 // ---------------------------------------------------------------------------
7910 
7912 {
7913  OnTimeArrivals = 0;
7914  LateArrivals = 0;
7915  EarlyArrivals = 0;
7916  OnTimePasses = 0;
7917  LatePasses = 0;
7918  EarlyPasses = 0;
7919  OnTimeDeps = 0;
7920  LateDeps = 0;
7921  MissedStops = 0;
7922  OtherMissedEvents = 0;
7923  UnexpectedExits = 0;
7924  NumFailures = 0;
7925  IncorrectExits = 0;
7926  SPADEvents = 0;
7927  SPADRisks = 0;
7928  CrashedTrains = 0;
7929  Derailments = 0;
7930  TotArrDepPass = 0;
7931  TotLateArrMins = 0;
7932  TotEarlyArrMins = 0;
7933  TotLatePassMins = 0;
7934  TotEarlyPassMins = 0;
7935  TotLateDepMins = 0;
7936  ExcessLCDownMins = 0;
7937  TTClockTime = 0; // added for v0.6
7939  // added at v1.3.0 to ensure false at start
7940  OpTimeToActUpdateCounter = 0; // new v2.2.0
7941  OpActionPanelVisible = false; // new v2.2.0
7942  // reset all message flags, stops them being given twice (shouldn't be needed here but add for safety) //new at v2.4.0
7943  SSHigh = false;
7944  MRSHigh = false;
7945  MRSLow = false;
7946  MassHigh = false;
7947  BFHigh = false;
7948  BFLow = false;
7949  PwrHigh = false;
7950  SigSHigh = false;
7951  SigSLow = false;
7952  randomize();
7953  // to seed rand() & random() with a random number (see UpdateTrain)
7954 }
7955 
7956 // ---------------------------------------------------------------------------
7957 
7959 {
7960  for(unsigned int x = 0; x < TrainVector.size(); x++)
7961  {
7962  TrainVectorAt(32, x).DeleteTrain(4);
7963  }
7964  TrainVector.clear();
7965 }
7966 
7967 // ---------------------------------------------------------------------------
7968 
7969 void TTrainController::LogEvent(AnsiString Str)
7970 {
7971  AnsiString FullStr = Utilities->TimeStamp() + "," + TTClockTime.FormatString("hh:nn:ss") + "," + Str;
7972 
7973  // restrict to last 1000 entries
7974  Utilities->EventLog.push_back(FullStr);
7975  if(Utilities->EventLog.size() > 1000)
7976  Utilities->EventLog.pop_front();
7977 }
7978 
7979 // ---------------------------------------------------------------------------
7980 
7982 {
7983  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",Operate");
7984  bool ClockState = Utilities->Clock2Stopped;
7985 
7986  Utilities->Clock2Stopped = true;
7987  // new section dealing with Snt & Snt-sh additions
7988  // BUT don't add trains if points or route flashing [conditions added for Version 0.6 as a result of Najamuddin's error - 15/01/11] - wait until next
7989  // clock tick after stops flashing
7991  {
7992  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
7993  {
7994  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
7995  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
7996  TActionEventType EventType = NoEvent;
7997  if(AVEntry0.Command == "Snt")
7998  {
7999  // calc below only for Snt & Snt-sh entries rather than all entries to save time
8000  const TActionVectorEntry &AVEntryLast = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
8001  int IncrementalMinutes = 0;
8002  int IncrementalDigits = 0;
8003  if(AVEntryLast.FormatType == Repeat)
8004  {
8005  IncrementalMinutes = AVEntryLast.RearStartOrRepeatMins;
8006  IncrementalDigits = AVEntryLast.FrontStartOrRepeatDigits;
8007  }
8008  if((AVEntryLast.FormatType == Repeat) && (TDEntry.NumberOfTrains < 2))
8009  {
8010  throw Exception("Error - Repeat entry && less than two trains for Snt entry: " + TDEntry.HeadCode);
8011  }
8012  // see above note
8013 
8014  for(int y = 0; y < TDEntry.NumberOfTrains; y++)
8015  {
8016  TTrainOperatingData &TTOD = TDEntry.TrainOperatingDataVector.at(y);
8017  if(TTOD.RunningEntry != NotStarted)
8018  continue;
8019  if(GetRepeatTime(2, AVEntry0.EventTime, y, IncrementalMinutes) > TTClockTime)
8020  break; // all the rest will also be greater
8021  AnsiString TrainHeadCode = GetRepeatHeadCode(22, TDEntry.HeadCode, y, IncrementalDigits);
8022  if(AddTrain(2, AVEntry0.RearStartOrRepeatMins, AVEntry0.FrontStartOrRepeatDigits, TrainHeadCode, TDEntry.StartSpeed, TDEntry.Mass,
8023  TDEntry.MaxRunningSpeed, TDEntry.MaxBrakeRate, TDEntry.PowerAtRail, "Timetable", &TDEntry, y, IncrementalMinutes, IncrementalDigits,
8024  TDEntry.SignallerSpeed, AVEntry0.SignallerControl, EventType))
8025  {
8026  TTOD.TrainID = TrainVector.back().TrainID;
8027  TTOD.RunningEntry = Running;
8028  }
8029  else if(EventType == FailTrainEntry)
8030  {
8031  break; // if a train can't enter no point checking any more repeats as they won't be able to enter either
8032  }
8033  }
8034  }
8035 
8036  if(AVEntry0.Command == "Snt-sh")
8037  // just start this once, shuttle repeats take care of restarts
8038  {
8039  // calc below only for Snt & Snt-sh entries rather than all entries to save time
8040  const TActionVectorEntry &AVEntryLast = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
8041  int IncrementalMinutes = 0;
8042  int IncrementalDigits = 0;
8043  if(AVEntryLast.FormatType == Repeat)
8044  {
8045  IncrementalMinutes = AVEntryLast.RearStartOrRepeatMins;
8046  IncrementalDigits = AVEntryLast.FrontStartOrRepeatDigits;
8047  }
8048  if((AVEntryLast.FormatType == Repeat) && (TDEntry.NumberOfTrains < 2))
8049  {
8050  throw Exception("Error - Repeat entry && less than two trains for Snt-sh entry: " + TDEntry.HeadCode);
8051  }
8052  // see above note
8053  TTrainOperatingData &TTOD = TDEntry.TrainOperatingDataVector.at(0);
8054  if(TTOD.RunningEntry == NotStarted)
8055  {
8056  if(AVEntry0.EventTime <= TTClockTime)
8057  {
8058  if(AddTrain(3, AVEntry0.RearStartOrRepeatMins, AVEntry0.FrontStartOrRepeatDigits, TDEntry.HeadCode, TDEntry.StartSpeed, TDEntry.Mass,
8059  TDEntry.MaxRunningSpeed, TDEntry.MaxBrakeRate, TDEntry.PowerAtRail, "Timetable", &TDEntry, 0, IncrementalMinutes, IncrementalDigits,
8060  TDEntry.SignallerSpeed, false, EventType))
8061  // false for SignallerControl
8062  {
8063  TTOD.TrainID = TrainVector.back().TrainID;
8064  TTOD.RunningEntry = Running;
8065  }
8066  else if(EventType == FailTrainEntry)
8067  {
8068  break; // if a train can't enter no point checking any more repeats as they won't be able to enter either
8069  }
8070  }
8071  }
8072  }
8073  }
8074  }
8075 
8076  // deal with running trains but abort if any vectors added, would probably be OK but don't risk a vector reallocation disrupting the
8077  // iteration, next cycle will catch up with any other pending updates
8078  if(!TrainVector.empty())
8079  {
8080  TrainAdded = false;
8081  AllRoutes->CallonVector.clear();
8082  // this will be rebuilt during the calls to UpdateTrain
8083  for(unsigned int x = 0; x < TrainVector.size(); x++)
8084  {
8085  TrainVectorAt(33, x).UpdateTrain(0);
8086  if(TrainAdded || TrainVectorAt(35, x).HasTrainGone())
8087  // added HasTrainGone() condition in v0.4c to prevent 2 trains both having TrainGone set
8088  // at the same time. That caused the error Craig Weekes reported in November 2010 where 2 trains exited at the same time, and later the TrainVector
8089  // iterates in reverse to erase the second train to have gone, but afterwards ReplotTrains iterates forwards and therefore replots the first train to
8090  // have gone and therefore sets the TrainIDOnElement value to the exited train, with nothing to reset it. Hovering the mouse over that element with
8091  // train information enabled causes an error because the track element thinks the train is still there, whereas it is missing from the TrainVector.
8092  {
8093  break;
8094  }
8095  }
8096  // set warning flags
8097  CrashWarning = false;
8098  DerailWarning = false;
8099  SPADWarning = false;
8100  CallOnWarning = false;
8101  SignalStopWarning = false;
8102  BufferAttentionWarning = false;
8103  TrainFailedWarning = false;
8104  for(int x = TrainVector.size() - 1; x >= 0; x--) // reverse because of erase
8105  {
8106  TTrain &Train = TrainVectorAt(34, x);
8107  if(Train.Crashed)
8108  // can't use background colours for crashed & derailed because same colour
8109  {
8110  CrashWarning = true;
8111  }
8112  else if(Train.Derailed)
8113  // can't use background colours for crashed & derailed because same colour
8114  {
8115  DerailWarning = true;
8116  }
8117  else if(Train.BackgroundColour == clSPADBackground)
8118  // use colour as that changes as soon as passes signal
8119  {
8120  SPADWarning = true;
8121  }
8122  else if(Train.BackgroundColour == clTrainFailedBackground)
8123  {
8124  TrainFailedWarning = true;
8125  }
8126  else if(Train.BackgroundColour == clCallOnBackground)
8127  // use colour as also stopped at signal
8128  {
8129  CallOnWarning = true;
8130  }
8131  else if(Train.BackgroundColour == clSignalStopBackground)
8132  // use colour to distinguish from call-on
8133  {
8134  SignalStopWarning = true;
8135  }
8136  else if(Train.BackgroundColour == clBufferAttentionNeeded)
8137  // use colour to distinguish from ordinary buffer stop
8138  {
8139  BufferAttentionWarning = true;
8140  }
8141  if(Train.HasTrainGone())
8142  {
8143  AnsiString Loc = "";
8144  bool ElementFound = false;
8145  TTrackElement TE;
8146  if(Train.LagElement > -1)
8147  {
8148  TE = Track->TrackElementAt(531, Train.LagElement);
8149  ElementFound = true;
8150  }
8151  else if(Train.MidElement > -1)
8152  {
8153  TE = Track->TrackElementAt(779, Train.MidElement);
8154  ElementFound = true;
8155  }
8156  else if(Train.LeadElement > -1)
8157  {
8158  TE = Track->TrackElementAt(780, Train.LeadElement);
8159  ElementFound = true;
8160  }
8161  if(ElementFound)
8162  {
8163  if(TE.ActiveTrackElementName != "")
8164  {
8165  Loc = TE.ActiveTrackElementName + ", track element " + TE.ElementID;
8166  }
8167  else
8168  {
8169  Loc = "track element " + TE.ElementID;
8170  }
8171  }
8172 
8173  TActionVectorEntry *AVEntryPtr = Train.ActionVectorEntryPtr;
8174  if((Train.SignallerRemoved) || (Train.JoinedOtherTrainFlag))
8175  // need above first because may also have ActionVectorEntryPtr == "Fer"
8176  {
8177  Train.UnplotTrain(9);
8178  // added at v1.3.0 to reset signals after train removed from an autosigsroute
8180  {
8183  }
8184  // end of addition
8185  AllRoutes->RebuildRailwayFlag = true;
8186  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode, to replot LCs
8187  // correctly after a crash
8188  }
8189  else if(AVEntryPtr->Command == "Fer")
8190  {
8191  bool CorrectExit = false;
8192  if(!AVEntryPtr->ExitList.empty())
8193  {
8194  for(TExitListIterator ELIT = AVEntryPtr->ExitList.begin(); ELIT != AVEntryPtr->ExitList.end(); ELIT++)
8195  {
8196  if(*ELIT == Train.LagElement)
8197  CorrectExit = true;
8198  }
8199  }
8200  if(CorrectExit)
8201  {
8202  Train.LogAction(19, Train.HeadCode, "", Leave, Loc, AVEntryPtr->EventTime, AVEntryPtr->Warning);
8203  }
8204  else
8205  {
8206  LogActionError(38, Train.HeadCode, "", FailIncorrectExit, Loc);
8207  }
8208  }
8209  else
8210  {
8211  if(!AVEntryPtr->SignallerControl)
8212  {
8213  LogActionError(26, Train.HeadCode, "", FailUnexpectedExitRailway, Loc);
8214  Train.SendMissedActionLogs(2, -2, AVEntryPtr);
8215  // -2 is marker for send messages for all remaining actions except Fer if present
8216  }
8217  else
8218  {
8219  Train.LogAction(31, Train.HeadCode, "", SignallerLeave, Loc, TDateTime(0), false); // false for Warning
8220  }
8221  }
8222  Train.TrainDataEntryPtr->TrainOperatingDataVector.at(Train.RepeatNumber).RunningEntry = Exited;
8223  Train.DeleteTrain(1);
8224  TrainVector.erase(TrainVector.begin() + x);
8225  ReplotTrains(1, Display);
8226  // to reset ElementIDs for remaining trains when have removed a train
8227  }
8228  }
8229  }
8230  else
8231  {
8232  // reset all flags in case last train removed with flag set
8233  CrashWarning = false;
8234  DerailWarning = false;
8235  SPADWarning = false;
8236  CallOnWarning = false;
8237  SignalStopWarning = false;
8238  BufferAttentionWarning = false;
8239  TrainFailedWarning = false;
8240  }
8241 
8242  // update OpTimeToActMultimap
8244  {
8246  // clears entries then adds values for running trains then for continuation entries
8247  }
8248 
8249  Utilities->Clock2Stopped = ClockState;
8250  Utilities->CallLogPop(723);
8251 }
8252 
8253 // ---------------------------------------------------------------------------
8255 {
8256  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",FinishedOperation");
8257  if(!TrainVector.empty())
8258  {
8259  for(int x = TrainVector.size() - 1; x >= 0; x--)
8260  {
8261  TrainVectorAt(50, x).DeleteTrain(2);
8262  }
8263  TrainVector.clear();
8264  }
8265  if(!TrainDataVector.empty())
8266  {
8267  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
8268  {
8269  TTrainDataEntry &TDEntry = TrainDataVector.at(x);
8270  for(int y = 0; y < TDEntry.NumberOfTrains; y++)
8271  {
8272  TTrainOperatingData &TOD = TDEntry.TrainOperatingDataVector.at(y);
8273  TOD.RunningEntry = NotStarted;
8274  TOD.TrainID = -1;
8275  TOD.EventReported = NoEvent;
8276  }
8277  }
8278  }
8279  Display->GetOutputLog1()->Caption = "";
8280  Display->GetOutputLog2()->Caption = "";
8281  Display->GetOutputLog3()->Caption = "";
8282  Display->GetOutputLog4()->Caption = "";
8283  Display->GetOutputLog5()->Caption = "";
8284  Display->GetOutputLog6()->Caption = "";
8285  Display->GetOutputLog7()->Caption = "";
8286  Display->GetOutputLog8()->Caption = "";
8287  Display->GetOutputLog9()->Caption = "";
8288  Display->GetOutputLog10()->Caption = "";
8289  Utilities->CallLogPop(1352);
8290 }
8291 
8292 // ---------------------------------------------------------------------------
8293 
8295 {
8296  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ReplotTrains");
8297  if(!TrainVector.empty())
8298  {
8299  for(unsigned int x = 0; x < TrainVector.size(); x++)
8300  {
8301  TrainVectorAt(51, x).PlotTrain(4, Disp);
8302  }
8303  }
8304  Utilities->CallLogPop(724);
8305 }
8306 
8307 // ---------------------------------------------------------------------------
8308 
8309 void TTrainController::WriteTrainsToImage(int Caller, Graphics::TBitmap *Bitmap)
8310 {
8311  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",WriteTrainsToImage");
8312  if(!TrainVector.empty())
8313  {
8314  for(unsigned int x = 0; x < TrainVector.size(); x++)
8315  {
8316  TrainVectorAt(61, x).WriteTrainToImage(0, Bitmap);
8317  }
8318  }
8319  Utilities->CallLogPop(1707);
8320 }
8321 
8322 // ---------------------------------------------------------------------------
8323 
8325 {
8326  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UnplotTrains");
8327  if(!TrainVector.empty())
8328  {
8329  for(unsigned int x = 0; x < TrainVector.size(); x++)
8330  {
8331  TrainVectorAt(52, x).UnplotTrain(10);
8332  }
8333  }
8335  Utilities->CallLogPop(725);
8336 }
8337 
8338 // ---------------------------------------------------------------------------
8339 
8340 bool TTrainController::AddTrain(int Caller, int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass, double MaxRunningSpeed,
8341  double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr, int RepeatNumber, int IncrementalMinutes,
8342  int IncrementalDigits, int SignallerSpeed, bool SignallerControl, TActionEventType &EventType)
8343 {
8344  LogEvent(AnsiString(Caller) + ",AddTrain," + AnsiString(RearPosition) + "," + AnsiString(FrontPosition) + "," + HeadCode + "," + AnsiString(StartSpeed) +
8345  "," + AnsiString(Mass) + "," + ModeStr);
8346  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",AddTrain," + AnsiString(RearPosition) + "," + AnsiString(FrontPosition) +
8347  "," + HeadCode + "," + AnsiString(StartSpeed) + "," + AnsiString(Mass) + "," + ModeStr + "," + HeadCode);
8348 
8349  int RearExitPos = -1;
8350 
8351  for(int x = 0; x < 4; x++)
8352  {
8353  if(Track->TrackElementAt(519, RearPosition).Conn[x] == FrontPosition)
8354  {
8355  RearExitPos = x;
8356  }
8357  }
8358  if(RearExitPos == -1)
8359  {
8360  throw Exception("Error, RearExit == -1 in AddTrain");
8361  }
8362 
8363  bool ReportFlag = true;
8364 
8365  // used to stop repeated messages from CheckStartAllowable when split failed
8366  if(TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported != NoEvent)
8367  ReportFlag = false;
8368 
8369  if(!CheckStartAllowable(0, RearPosition, RearExitPos, HeadCode, ReportFlag, EventType))
8370  { // messages sent to performance log in CheckStartAllowable if ReportFlag true
8371  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported = EventType;
8372  Utilities->CallLogPop(938);
8373  return false;
8374  }
8375 
8376  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported = NoEvent;
8377  TTrainMode TrainMode = NoMode;
8378 
8379  if(ModeStr == "Timetable")
8380  TrainMode = Timetable;
8381  // all else gives 'None', 'Signaller' set within program
8382 
8383  if(MaxRunningSpeed < 10)
8384  {
8385  MaxRunningSpeed = 10; // added at v0.6 to avoid low max speeds
8386  }
8387  if(SignallerSpeed < 10)
8388  SignallerSpeed = 10; // added at v0.6 to avoid low max speeds
8389  TTrain *NewTrain = new TTrain(0, RearPosition, RearExitPos, HeadCode, StartSpeed, Mass, MaxRunningSpeed, MaxBrakeRate, PowerAtRail, TrainMode,
8390  TrainDataEntryPtr, RepeatNumber, IncrementalMinutes, IncrementalDigits, SignallerSpeed);
8391 
8392  NewTrain->ActionVectorEntryPtr = &(TrainDataEntryPtr->ActionVector.at(0));
8393  // initialise here rather than in TTrain constructor as create trains
8394  // with Null TrainDataEntryPtr when loading session trains
8395  if(SignallerControl)
8396  {
8397  NewTrain->TimetableFinished = true;
8398  NewTrain->SignallerStoppingFlag = false;
8399  NewTrain->TrainMode = Signaller;
8400  if(NewTrain->MaxRunningSpeed > NewTrain->SignallerMaxSpeed)
8401  {
8402  NewTrain->MaxRunningSpeed = NewTrain->SignallerMaxSpeed;
8403  }
8405  }
8406 
8407  // deal with starting conditions:-
8408  // unlocated Snt: just report entry & advance pointer
8409  // located Snt or Sfs: set station conditions as would if had reached stop point in Update(), & advance the ActionVectorEntryPtr
8410  // Sns doesn't need a new train
8411  if(NewTrain->ActionVectorEntryPtr->LocationName != "")
8412  // covers all above located starts
8413  // if location of Snt was a station (that is set as LocationName, i.e. not just any station) that isn't next departure station then
8414  // wouldn't have accepted the timetable
8415  {
8416  // first check if LeadElement (can't access LeadElement directly yet as not set, use FrontPosition instead) is buffers, note that
8417  // StoppedAtBuffers is set in UpdateTrain()
8418  if(Track->TrackElementAt(520, FrontPosition).TrackType == Buffers)
8419  // buffer end must be ahead of train or would have failed start position check
8420  {
8421  NewTrain->StoppedAtLocation = true;
8422  NewTrain->PlotStartPosition(0);
8424  NewTrain->LogAction(20, NewTrain->HeadCode, "", Create, NewTrain->ActionVectorEntryPtr->LocationName, NewTrain->ActionVectorEntryPtr->EventTime,
8425  NewTrain->ActionVectorEntryPtr->Warning);
8426  if(!SignallerControl) // don't advance if SignalControlEntry
8427  {
8428  NewTrain->ActionVectorEntryPtr++;
8429  // should be a command, could be a location departure but if so can't depart so set 'Hold' anyway
8430  }
8431  NewTrain->LastActionTime = TTClockTime;
8432  }
8433  // else a through station stop
8434  else
8435  {
8436  NewTrain->StoppedAtLocation = true;
8437  NewTrain->PlotStartPosition(10);
8439  NewTrain->LogAction(21, NewTrain->HeadCode, "", Create, NewTrain->ActionVectorEntryPtr->LocationName, NewTrain->ActionVectorEntryPtr->EventTime,
8440  NewTrain->ActionVectorEntryPtr->Warning);
8441  if(!SignallerControl) // don't advance if SignalControlEntry
8442  {
8443  NewTrain->ActionVectorEntryPtr++;
8444  }
8445  NewTrain->LastActionTime = TTClockTime;
8446  }
8447  }
8448  else // unlocated entry (i.e. not a stop entry, but could still be at a named location)
8449  {
8450  NewTrain->PlotStartPosition(11);
8451  TTrackElement TE = Track->TrackElementAt(530, NewTrain->RearStartElement);
8452  AnsiString Loc = "";
8453  if(TE.ActiveTrackElementName != "")
8454  {
8455  Loc = TE.ActiveTrackElementName + ", track element " + TE.ElementID;
8456  }
8457  else
8458  {
8459  Loc = "track element " + TE.ElementID;
8460  }
8461  if(TE.TrackType == Continuation)
8462  {
8463  NewTrain->LogAction(22, NewTrain->HeadCode, "", Enter, Loc, NewTrain->ActionVectorEntryPtr->EventTime, NewTrain->ActionVectorEntryPtr->Warning);
8464  }
8465  else
8466  {
8467  NewTrain->LogAction(23, NewTrain->HeadCode, "", Create, Loc, NewTrain->ActionVectorEntryPtr->EventTime, NewTrain->ActionVectorEntryPtr->Warning);
8468  }
8469  if(!SignallerControl) // don't advance if SignalControlEntry
8470  {
8471  NewTrain->ActionVectorEntryPtr++;
8472  }
8473  NewTrain->LastActionTime = TTClockTime;
8474  // no need to set LastActionTime for an unlocated entry
8475  }
8476 
8477  // cancel a wrong-direction route if either element of train starts on one
8478  if(NewTrain->LeadElement > -1)
8479  {
8480  NewTrain->CheckAndCancelRouteForWrongEndEntry(3, NewTrain->LeadElement, NewTrain->LeadEntryPos);
8481  }
8482  if(NewTrain->MidElement > -1)
8483  {
8484  NewTrain->CheckAndCancelRouteForWrongEndEntry(4, NewTrain->MidElement, NewTrain->MidEntryPos);
8485  }
8486 
8487  // set signals for a right-direction autosigs route for either element of train on one
8488  // erase elements back to start for a non-autosigs route & check if an autosigs route immediately behind it, and if so set its signals
8489  // note that all but autosigs routes become part of a single route, so there can only be an autosigs route behind the non-autosigs route
8490  int RouteNumber = -1;
8491  bool SignalsSet = false;
8492 
8493  if(NewTrain->LeadElement > -1)
8494  {
8495  if(AllRoutes->GetRouteTypeAndNumber(13, NewTrain->LeadElement, NewTrain->LeadEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
8496  { // below added in place of SetRouteSignals in v2.4.0 as don't want to set signals from start of route for a new train addition
8497  int RouteStartPosition;
8498  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
8499  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(21, Track->TrackElementAt(955, FrontPosition).HLoc,
8500  Track->TrackElementAt(956, FrontPosition).VLoc, SecondPair);
8501  if(FirstPair.first == RouteNumber)
8502  {
8503  RouteStartPosition = FirstPair.second;
8504  }
8505  else if(SecondPair.first == RouteNumber)
8506  {
8507  RouteStartPosition = SecondPair.second;
8508  }
8509  else
8510  {
8511  throw Exception("Error, RouteNumber not found in Route2MultiMap in 1st of 2 calls to SetAllRearwardsSignals in AddTrain");
8512  }
8513  AllRoutes->SetAllRearwardsSignals(10, 0, RouteNumber, RouteStartPosition);
8514  SignalsSet = true;
8515  // AllRoutes->GetFixedRouteAt(, RouteNumber).SetRouteSignals(); above substituted in v2.4.0
8516  }
8517  else if(RouteNumber > -1) // non-autosigsroute
8518  {
8519  TPrefDirElement TempPDE = AllRoutes->GetFixedRouteAt(181, RouteNumber).GetFixedPrefDirElementAt(194, 0);
8520  int FirstTVPos = TempPDE.GetTrackVectorPosition();
8521  int FirstELinkPos = TempPDE.GetELinkPos();
8522  while(TempPDE.GetTrackVectorPosition() != (unsigned int)(NewTrain->LeadElement))
8523  {
8524  AllRoutes->RemoveRouteElement(16, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
8525  TempPDE = AllRoutes->GetFixedRouteAt(182, RouteNumber).GetFixedPrefDirElementAt(195, 0);
8526  }
8527  if(TempPDE.GetTrackVectorPosition() == (unsigned int)(NewTrain->LeadElement))
8528  {
8529  AllRoutes->RemoveRouteElement(17, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
8530  // remove the last element under LeadElement
8531  }
8532  AllRoutes->RebuildRailwayFlag = true;
8533  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode
8534  // now deal with a rear linked autosigs route
8535  if(Track->TrackElementAt(820, FirstTVPos).Conn[FirstELinkPos] > -1)
8536  {
8537  int LinkedRouteNumber = -1;
8538  if(AllRoutes->GetRouteTypeAndNumber(17, Track->TrackElementAt(821, FirstTVPos).Conn[FirstELinkPos],
8539  Track->TrackElementAt(822, FirstTVPos).ConnLinkPos[FirstELinkPos], LinkedRouteNumber) == TAllRoutes::AutoSigsRoute)
8540  {
8541  AllRoutes->GetFixedRouteAt(169, LinkedRouteNumber).SetRouteSignals(0);
8542  // this is ok as here we are setting signals from the start of the route
8543  }
8544  }
8545  SignalsSet = true;
8546  }
8547  }
8548  if(NewTrain->MidElement > -1)
8549  // if entering at a continuation MidElement == -1
8550  { // this is included in case a train starts with LeadElement on no route and MidElement on a route
8551  if(!SignalsSet)
8552  {
8553  RouteNumber = -1;
8554  if(AllRoutes->GetRouteTypeAndNumber(14, NewTrain->MidElement, NewTrain->MidEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
8555  { // below added in place of SetRouteSignals in v2.4.0 as don't want to set signals from start of route for a new train addition
8556  int RouteStartPosition;
8557  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
8558  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(22, Track->TrackElementAt(957, RearPosition).HLoc,
8559  Track->TrackElementAt(958, RearPosition).VLoc, SecondPair);
8560  if(FirstPair.first == RouteNumber)
8561  {
8562  RouteStartPosition = FirstPair.second;
8563  }
8564  else if(SecondPair.first == RouteNumber)
8565  {
8566  RouteStartPosition = SecondPair.second;
8567  }
8568  else
8569  {
8570  throw Exception("Error, RouteNumber not found in Route2MultiMap in 2nd of 2 calls to SetAllRearwardsSignals in AddTrain");
8571  }
8572  AllRoutes->SetAllRearwardsSignals(11, 0, RouteNumber, RouteStartPosition);
8573  SignalsSet = true;
8574  // AllRoutes->GetFixedRouteAt(, RouteNumber).SetRouteSignals(); above substituted in v2.4.0
8575  }
8576  else if(RouteNumber > -1) // non-autosigsroute
8577  {
8578  TPrefDirElement TempPDE = AllRoutes->GetFixedRouteAt(184, RouteNumber).GetFixedPrefDirElementAt(196, 0);
8579  int FirstTVPos = TempPDE.GetTrackVectorPosition();
8580  int FirstELinkPos = TempPDE.GetELinkPos();
8581  while(TempPDE.GetTrackVectorPosition() != (unsigned int)(NewTrain->MidElement))
8582  {
8583  AllRoutes->RemoveRouteElement(18, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
8584  TempPDE = AllRoutes->GetFixedRouteAt(185, RouteNumber).GetFixedPrefDirElementAt(197, 0);
8585  }
8586  if(TempPDE.GetTrackVectorPosition() == (unsigned int)(NewTrain->MidElement))
8587  {
8588  AllRoutes->RemoveRouteElement(19, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
8589  // remove the last element under LeadElement
8590  }
8591  AllRoutes->RebuildRailwayFlag = true;
8592  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode
8593  // now deal with a rear linked autosigs route
8594  if(Track->TrackElementAt(823, FirstTVPos).Conn[FirstELinkPos] > -1)
8595  {
8596  int LinkedRouteNumber = -1;
8597  if(AllRoutes->GetRouteTypeAndNumber(19, Track->TrackElementAt(824, FirstTVPos).Conn[FirstELinkPos],
8598  Track->TrackElementAt(825, FirstTVPos).ConnLinkPos[FirstELinkPos], LinkedRouteNumber) == TAllRoutes::AutoSigsRoute)
8599  {
8600  AllRoutes->GetFixedRouteAt(170, LinkedRouteNumber).SetRouteSignals(1);
8601  // this is ok as now we are setting signals from the start of the route
8602  }
8603  }
8604  }
8605  }
8606  }
8607 
8608  TrainVector.push_back(*NewTrain);
8609  Utilities->CallLogPop(731);
8610  return true;
8611 }
8612 
8613 // ---------------------------------------------------------------------------
8614 
8615 int TTrainController::EntryPos(int Caller, int TrainIDIn, int TrackVectorNumber)
8616 {
8617  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",EntryPos," + AnsiString(TrainIDIn) + "," +
8618  AnsiString(TrackVectorNumber));
8619  int VecPos = -1;
8620 
8621  for(unsigned int x = 0; x < TrainVector.size(); x++)
8622  {
8623  if(TrainVectorAt(1, x).TrainID == TrainIDIn)
8624  {
8625  VecPos = x;
8626  }
8627  }
8628  if(VecPos == -1)
8629  {
8630  throw Exception("Error, VecPos not set in EntryPos");
8631  }
8632  if(TrainVectorAt(2, VecPos).LeadElement == TrackVectorNumber)
8633  {
8634  Utilities->CallLogPop(734);
8635  return TrainVectorAt(3, VecPos).LeadEntryPos;
8636  }
8637  else if(TrainVectorAt(4, VecPos).MidElement == TrackVectorNumber)
8638  {
8639  Utilities->CallLogPop(735);
8640  return TrainVectorAt(5, VecPos).MidEntryPos;
8641  }
8642  else if(TrainVectorAt(6, VecPos).LagElement == TrackVectorNumber)
8643  {
8644  Utilities->CallLogPop(736);
8645  return TrainVectorAt(7, VecPos).LagEntryPos;
8646  }
8647  Utilities->CallLogPop(737);
8648  return -1;
8649 }
8650 
8651 // ---------------------------------------------------------------------------
8652 
8654 {
8655  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainVectorAtIdent," + AnsiString(TrainID));
8656  for(unsigned int x = 0; x < TrainVector.size(); x++)
8657  {
8658  if(TrainVectorAt(53, x).TrainID == TrainID)
8659  {
8660  Utilities->CallLogPop(738);
8661  return TrainVectorAt(54, x);
8662  }
8663  }
8664  throw Exception("Error - No Train identified in TrainVectorAtIdent with ID = " + AnsiString(TrainID));
8665 }
8666 
8667 // ---------------------------------------------------------------------------
8668 
8669 bool TTrainController::TrainExistsAtIdent(int Caller, int TrainID)
8670  // return true if find the train (added at v2.4.0 as can select a removed train
8671  // in OAListBox before it updates - reported by LiWinDom in error report 23/04/20)
8672 {
8673  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainExistsAtIdent," + AnsiString(TrainID));
8674  for(unsigned int x = 0; x < TrainVector.size(); x++)
8675  {
8676  if(TrainVectorAt(69, x).TrainID == TrainID)
8677  {
8678  Utilities->CallLogPop(2152);
8679  return true;
8680  }
8681  }
8682  Utilities->CallLogPop(2153);
8683  return false;
8684 }
8685 
8686 // ---------------------------------------------------------------------------
8687 
8688 TDateTime TTrainController::GetControllerTrainTime(int Caller, TDateTime Time, int RepeatNumber, int IncrementalMinutes)
8689 {
8690  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetControllerTrainTime," + AnsiString(RepeatNumber) + "," +
8691  Utilities->Format96HHMMSS(Time));
8692  TDateTime RepeatTime = TrainController->GetRepeatTime(47, Time, RepeatNumber, IncrementalMinutes);
8693 
8694  Utilities->CallLogPop(2061);
8695  return RepeatTime;
8696 }
8697 
8698 // ---------------------------------------------------------------------------
8699 
8700 AnsiString TTrainController::ContinuationEntryFloatingTTString(int Caller, TTrainDataEntry *TTDEPtr, int RepNum, int IncMins, int IncDig)
8701  // Enter with Ptr pointing to first action to be listed (i.e. next action)
8702 {
8703  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ContinuationEntryFloatingTTString" + "," + TTDEPtr->HeadCode);
8704  AnsiString RetStr = "", PartStr = "";
8705  int Count = 0;
8706  TActionVectorIterator Ptr = TTDEPtr->ActionVector.begin();
8707 
8708  Ptr--; // because incremented at start of loop
8709  do
8710  {
8711  Ptr++;
8712  if((Ptr->Command != "") && (Ptr->Command[1] == 'S'))
8713  {
8714  continue; // move past the starting entry
8715  }
8716  if((Ptr->FormatType == Repeat) || Ptr >= TTDEPtr->ActionVector.end())
8717  break;
8718  if(Ptr->SignallerControl)
8719  {
8720  RetStr = "Train under signaller control";
8721  break;
8722  }
8723  if(Ptr->FormatType == TimeTimeLoc)
8724  {
8725  if(Ptr->ArrivalTime == Ptr->DepartureTime)
8726  {
8727  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(0, Ptr->ArrivalTime, RepNum, IncMins)) + ": Arrive & depart from " + Ptr->LocationName;
8728  }
8729  else
8730  {
8731  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(1, Ptr->ArrivalTime, RepNum, IncMins)) + ": Arrive at " + Ptr->LocationName + '\n' +
8732  Utilities->Format96HHMM(GetControllerTrainTime(2, Ptr->DepartureTime, RepNum, IncMins)) + ": Depart from " + Ptr->LocationName;
8733  Count++; // because there are 2 entries
8734  }
8735  }
8736  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
8737  {
8738  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(3, Ptr->ArrivalTime, RepNum, IncMins)) + ": Arrive at " + Ptr->LocationName;
8739  }
8740  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
8741  {
8742  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(4, Ptr->DepartureTime, RepNum, IncMins)) + ": Depart from " + Ptr->LocationName;
8743  }
8744  else if(Ptr->FormatType == PassTime) // new
8745  {
8746  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(5, Ptr->EventTime, RepNum, IncMins)) + ": Pass " + Ptr->LocationName;
8747  }
8748  else if(Ptr->Command == "Fns")
8749  {
8750  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(6, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
8751  TrainController->GetRepeatHeadCode(46, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
8752  PartStr = ControllerCheckNewServiceDepartureTime(0, Ptr, RepNum, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to PartStr
8753  }
8754  else if(Ptr->Command == "F-nshs")
8755  {
8756  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(7, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
8757  Ptr->NonRepeatingShuttleLinkHeadCode + " at " + Ptr->LocationName;
8758  PartStr = ControllerCheckNewServiceDepartureTime(1, Ptr, 0, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
8759  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
8760  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
8761  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
8762  }
8763 //Since this is a new continuation entry service it can't be Fns-sh or Frh-sh but leave these in for consistency with TTrain::FloatingTimetableString
8764  else if((Ptr->Command == "Fns-sh") && (RepNum < (TTDEPtr->NumberOfTrains - 1))) // not the last repeat number
8765  {
8766  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(8, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
8767  TrainController->GetRepeatHeadCode(47, Ptr->OtherHeadCode, RepNum + 1, IncDig) + " at " + Ptr->LocationName;
8768  // use RepNum+1 because it's the repeat number of the NEXT shuttle service that is relevant
8769  PartStr = ControllerCheckNewServiceDepartureTime(2, Ptr, RepNum + 1, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
8770  }
8771  else if((Ptr->Command == "Fns-sh") && (RepNum >= (TTDEPtr->NumberOfTrains - 1))) // last repeat number
8772  {
8773  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(9, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
8774  Ptr->NonRepeatingShuttleLinkHeadCode, + " at " + Ptr->LocationName;
8775  PartStr = ControllerCheckNewServiceDepartureTime(3, Ptr, 0, TTDEPtr, Ptr->NonRepeatingShuttleLinkEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
8776  }
8777  else if((Ptr->Command == "Frh-sh") && (RepNum < (TTDEPtr->NumberOfTrains - 1))) // not the last repeat number
8778  {
8779  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(10, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
8780  TrainController->GetRepeatHeadCode(48, Ptr->OtherHeadCode, RepNum + 1, IncDig) + " at " + Ptr->LocationName;
8781  // use RepNum+1 because it's the repeat number of the NEXT shuttle service that is relevant
8782  PartStr = ControllerCheckNewServiceDepartureTime(4, Ptr, RepNum + 1, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
8783  }
8784  else if((Ptr->Command == "Frh-sh") && (RepNum >= (TTDEPtr->NumberOfTrains - 1))) // last repeat number
8785  {
8786  PartStr = "Terminate at " + Ptr->LocationName;
8787  }
8788  else if(Ptr->Command == "Frh")
8789  {
8790  PartStr = "Terminate at " + Ptr->LocationName;
8791  }
8792  else if(Ptr->Command == "Fer")
8793  {
8794  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(11, Ptr->EventTime, RepNum, IncMins)) + ": Exit railway" +
8795  TrainController->GetExitLocationAndAt(3, Ptr->ExitList);
8796  }
8797  else if(Ptr->Command == "Fjo")
8798  {
8799  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(12, Ptr->EventTime, RepNum, IncMins)) + ": Join " + TrainController->GetRepeatHeadCode(49,
8800  Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
8801  }
8802  else if(Ptr->Command == "jbo")
8803  {
8804  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(13, Ptr->EventTime, RepNum, IncMins)) + ": Joined by " + TrainController->GetRepeatHeadCode
8805  (50, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
8806  }
8807  else if(Ptr->Command == "fsp")
8808  {
8809  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(14, Ptr->EventTime, RepNum, IncMins)) + ": Front split to " +
8810  TrainController->GetRepeatHeadCode(51, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
8811  }
8812  else if(Ptr->Command == "rsp")
8813  {
8814  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(15, Ptr->EventTime, RepNum, IncMins)) + ": Rear split to " +
8815  TrainController->GetRepeatHeadCode(52, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
8816  }
8817  else if(Ptr->Command == "cdt")
8818  {
8819  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(16, Ptr->EventTime, RepNum, IncMins)) + ": Change direction at " + Ptr->LocationName;
8820  }
8821  if(RetStr != "")
8822  RetStr = RetStr + '\n' + PartStr;
8823  else
8824  RetStr = PartStr;
8825  Count++;
8826  }
8827  while(Ptr < TTDEPtr->ActionVector.end() && (Count < 33) && ((Ptr->Command == "") || ((Ptr->Command != "") && (Ptr->Command[1] != 'F'))));
8828  // limit of 33 allows a max of 34 entries (may have gone from 32 to 34 because of a TimeTimeLoc), which with track and train status gives
8829  // a max of 48 lines, at 13 pixels each, = 624 pixels & screen height has 641 so will fit comfortably. Also 34 timetable entries is as far
8830  // forward as anyone should wish to see without looking at the full timetable
8831  Utilities->CallLogPop(2072);
8832  return RetStr;
8833 }
8834 
8835 // ---------------------------------------------------------------------------
8836 
8837 AnsiString TTrainController::ControllerCheckNewServiceDepartureTime(int Caller, TActionVectorIterator Ptr, int RptNum, TTrainDataEntry *TDEPtr, TTrainDataEntry *LinkedTrainDataPtr, int IncrementalMinutes, AnsiString RetStr)
8838 {
8839  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TDEPtr->ActionVector.front()) + ","
8840  + AnsiString(RptNum) + ",ControllerCheckNewServiceDepartureTime," + TDEPtr->HeadCode);
8841  AnsiString DepTime = "", EventTime = "";
8842  bool CDTFlag = false; //reports if train changes direction before departs
8843  TActionVector NewServiceAV = LinkedTrainDataPtr->ActionVector;
8844  for(TActionVectorIterator AVI = NewServiceAV.begin(); AVI < NewServiceAV.end(); AVI++)
8845  {
8846  if(AVI->Command == "cdt")
8847  {
8848  CDTFlag = !CDTFlag; //toggles flag - allows for there being more than one cdt before departure
8849  continue;
8850  }
8851  if((AVI->Command == "fsp") || (AVI->Command == "rsp"))
8852  {
8853  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(21, AVI->EventTime, RptNum, IncrementalMinutes));
8854  RetStr += "\nNew service splits at " + EventTime;
8855  Utilities->CallLogPop(2237);
8856  return RetStr;
8857  }
8858  if(AVI->Command == "jbo")
8859  {
8860  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(22, AVI->EventTime, RptNum, IncrementalMinutes));
8861  RetStr += "\nNew service joined by " + AVI->OtherHeadCode + " at " + EventTime;
8862  Utilities->CallLogPop(2238);
8863  return RetStr;
8864  }
8865  if((AVI->FormatType == TimeLoc) && (AVI->DepartureTime > TDateTime(-1))) //departure time set
8866  {
8867  DepTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(23, AVI->DepartureTime, RptNum, IncrementalMinutes));
8868  if(CDTFlag)
8869  {
8870  RetStr += "\nNew service changes direction then departs at " + DepTime;
8871  }
8872  else
8873  {
8874  RetStr += "\nNew service departs at " + DepTime;
8875  }
8876  Utilities->CallLogPop(2239);
8877  return RetStr;
8878  }
8879  }
8880  Utilities->CallLogPop(2223);
8881  return RetStr;
8882 }
8883 
8884 // ---------------------------------------------------------------------------
8885 // $$$$$$$$$$$$$$$$$$$$$$ Start of Timetable Functions $$$$$$$$$$$$$$$$$$$$$$$$
8886 /*
8887  Note: The terms 'action' and 'entry' have been used freely for individual code lines within services in comments & in variable names, but
8888  for messages and in the manual and help files the term Entry is reserved for a complete service or train (i.e. an entry in the timetable),
8889  and 'event' is reserved for and individual code line within a service. Repeats use the term 'item' if they use any at all.
8890 
8891  In references to 'HeadCode' can have an optional prefix - up to 4 additional characters that can be anything, so long as last 4 digits
8892  represent the headcode. This allows links to be uniquely identified regardless of the headcode - so can have same headcodes as often as
8893  user wishes
8894 
8895  Prior to start time, anything except a line beginning with a time [...leading spaces...] HH:MM is ignored - can be
8896  descriptive text or anything user wishes
8897  A time on its own line [HH:MM], with or without leading spaces, but with anything following it before the CR (which will
8898  be ignored) is taken as the timetable start time.
8899  Thereafter there must be text on every line in the timetable, as the first blank line (or end of file) will be taken as the end of the
8900  timetable. Text can follow the 'end of timetable' blank line if the user wishes.
8901  A line within the timetable beginning with '*', with or without leading spaces, is ignored. Such lines can add text
8902  within the timetable if required.
8903  Timetable entries consist of one line per headcode (i.e. per service, not necessarily per train, as one train can run several different
8904  services)
8905  Each line starts with HeadCode & full train information for a new train (Snt or Snt-sh), or, for a continuing service
8906  (Sfs, Sns, Sns-sh or Sns-fsh), can have (a) Headcode only or (b) HeadCode + Description, nothing else
8907 
8908  All leading & trailing spaces before & after a line or any entry in a line are stripped off - these can be included to make reading a
8909  text timetable file easier
8910 
8911  form:-
8912  HeadCode[;Description (plain text, no commas or semicolons)][;StartSpeed(kph); MaxRunningSpeed(kph); Mass(tonnes, prog converts to kg);
8913  MaxBrakeRate(tonnes force, prog converts to m/s/s); & gross power(kW, prog converts to power at rail in w)
8914  then multiple entries, separated by commas, of the form:-
8915 
8916  HH:MM;Snt;RearStartIdent FrontStartIdent }StartNew }
8917  HH:MM;Snt-sh;RearStartIdent FrontStartIdent;Fsh HeadCode }SNTShuttle }
8918  HH:MM;Sns-sh;Fxx-sh HeadCode;F-nshs HeadCode (non-repeating)}SNSShuttle }
8919 
8920  HH:MM;Command;HeadCode (Sfs Sns jbo fsp rsp Fns Fjo Frh-sh) }TimeCmdHeadCode } Train action entries
8921  HH:MM;F-nshs;NonRepeatingShuttleLinkHeadCode }FNSNonRepeatToShuttle }
8922  HH:MM;Sns-fsh;NonRepeatingShuttleLinkHeadCode }SNSNonRepeatFromShuttle }
8923 
8924  HH:MM;Command (cdt) }TimeCmd }
8925  HH:MM;Location (arr & dep) }TimeLoc }
8926  HH:MM;HH:MM;Location }TimeTimeLoc }
8927  HH:MM;pas;Location }PassTime }
8928  HH:MM;Fns-sh;Snx-sh HeadCode;Sns-fsh HeadCode (non-rep) }FSHNewService }
8929  HH:MM;Fer;set of allowable IDs }ExitRailway }
8930  Command (Frh only) }FinRemHere }
8931 
8932  R;mm;dd;nn. Repeat Repeat entry
8933 
8934  Formats:
8935 
8936  Command only: Frh
8937  Time;Command: cdt
8938  Time;Command;Headcode: Sfs Sns jbo fsp rsp Fns Fjo Frh-sh F-nshs Sns-fsh
8939  Time;Command;2 Element IDs: Snt
8940  Time;Comand;n Element IDs: Fer
8941  Time;Command;rep Headcode;nonrep Headcode: Sns-sh Fns-sh
8942  Time;Command;2 Element IDs;Headcode Snt-sh
8943  Time;Command;Location pas
8944  Time;Location Arr Dep
8945  Time;Time;Location Arr & dep together
8946 
8947  9 Single entries: Snt (located or unlocated); pas; cdt; TimeLoc arr & dep; TimeTimeLoc; Fer; Frh
8948 
8949  9 1x Linked entries: Non-shuttle: fsp or rsp -> Sfs; Fns -> Sns; Fjo -> jbo; times must match, headcodes must match
8950  Shuttle: F-nshs -> Sns-sh: times match, F-nshs HeadCode matches Sns-sh 2nd Headcode;
8951  Fns-sh -> Sns-fsh: Fns-sh time + all repeats = Sns-fsh time, Fns-sh 2nd headcode matches Sns-fsh Headcode
8952 
8953  4 2x Linked entries, all shuttles:
8954 
8955  Frh-sh -> Snt-sh: Frh-sh time = Snt-sh time + 1 repeat while repeating, Frh-sh Headcode = Snt-sh Headcode;
8956  -> Sns-sh: Frh-sh time = Sns-sh time + 1 repeat while repeating, Frh-sh Headcode = Sns-sh 1st Headcode;
8957  -> Remain Here (at finish location after all repeats)
8958  Fns-sh -> Snt-sh: Frh-sh time = Snt-sh time + 1 repeat while repeating, Fns-sh 1st Headcode = Snt-sh Headcode
8959  -> Sns-sh: Frh-sh time = Sns-sh time + 1 repeat while repeating, Fns-sh 1st Headcode = Sns-sh 1st Headcode
8960 
8961  Allowable successors:-
8962 
8963  Successor state Type
8964 
8965  Snt located AtLoc ) Snt AtLoc successors: TimeLoc dep/jbo/fsp/rsp/cdt/Frh/Fns/Fjo/Frh-sh/Fns-sh/F-nshs;
8966  Snt Unlocated Moving ) Snt Moving successors: TimeLoc arr/TimeTimeLoc/pas/Fer;
8967  Sfs AtLoc )
8968  Sns AtLoc ) Start
8969  Sns-fsh AtLoc )
8970  Snt-sh AtLoc )
8971  Sns-sh AtLoc )
8972 
8973  pas Moving )
8974  jbo AtLoc )
8975  fsp AtLoc )
8976  rsp AtLoc ) Intermediate
8977  cdt AtLoc )
8978  TimeLoc arr Moving (bef) )
8979  TimeLoc dep AtLoc (bef) )
8980  TimeTimeLoc Moving )
8981 
8982  Fns Repeat/Nothing)
8983  Fjo Repeat/Nothing)
8984  Frh Repeat/Nothing)
8985  Fer Repeat/Nothing) Finish
8986  Frh-sh Repeat )
8987  Fns-sh Repeat )
8988  F-nshs Nothing )
8989 
8990  Descriptions:
8991  Snt New train
8992  Sfs New service from split
8993  Sns New service from another service
8994  Sns-fsh New non-repeating service from a shuttle service
8995  Snt-sh New shuttle train at a timetabled stop
8996  Sns-sh New shuttle service from a feeder service
8997 
8998  pas Pass
8999  jbo Be joined by another train
9000  fsp Front split
9001  rsp Rear split
9002  cdt Change direction of train
9003  TimeLoc arr Arrival
9004  TimeLoc dep Departure
9005  TimeTimeLoc Arrival and departure
9006 
9007  Fns Finish & form a new service
9008  Fjo Finish & join another train
9009  Frh Finish & remain here
9010  Fer Finish & exit railway
9011  Frh-sh Finish & repeat shuttle, finally remain here
9012  Fns-sh Finish & repeat shuttle, finally form a non-repeating service
9013  F-nshs Finish & form a shuttle feeder service
9014 */
9015 
9016 bool TTrainController::TimetableIntegrityCheck(int Caller, char *FileName, bool GiveMessages, bool CheckLocationsExistInRailway) // true for success
9017 {
9018  // Error messages mainly given in called functions, five are given here - empty file; inability to find a start time; timetable containing
9019  // a line that is too long; timetable containing too few lines; and timetable failed to open.
9020  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TimetableIntegrityCheck," + AnsiString(FileName));
9021  // new for v0.2b
9022  // compile ActiveTrackElementNameMap
9023  TTrack::TActiveTrackElementNameMapEntry ActiveTrackElementNameMapEntry;
9024 
9026  for(unsigned int x = 0; x < Track->TrackVector.size(); x++)
9027  {
9028  // if((Track->TrackVector.at(x).ActiveTrackElementName != "") && (Track->TrackVector.at(x).TrackType != Continuation))
9029  if((Track->TrackVector.at(x).ActiveTrackElementName != "") && (Track->ContinuationNameMap.find(Track->TrackVector.at(x).ActiveTrackElementName))
9030  == Track->ContinuationNameMap.end())
9031  { // exclude any name that appears in a continuation, error message given in tt validation if try to include such a name in a tt
9032  ActiveTrackElementNameMapEntry.first = Track->TrackVector.at(x).ActiveTrackElementName;
9033  ActiveTrackElementNameMapEntry.second = 0; // this is a dummy value
9034  Track->ActiveTrackElementNameMap.insert(ActiveTrackElementNameMapEntry);
9035  }
9036  }
9038  // end of new section
9039  std::ifstream TTBLFile(FileName, std::ios_base::binary);
9040 
9041  // binary mode so the "\r\n" pairs stay as they are rather than being entered as '\n'
9042  if(TTBLFile.is_open())
9043  {
9044  char *TrainTimetableString = new char[10000];
9045  // enough for over 200 stations, should be adequate!
9046  bool EndOfFile = false;
9047  int Count = 0;
9048  // counts 'relevant' lines, i.e ignores any before the start time on its own line
9049  TTBLFile.getline(TrainTimetableString, 10000, '\0');
9050  // delimiter is '\0' as it's an AnsiString
9051  if(TTBLFile.eof() && (TrainTimetableString[0] == '\0'))
9052  // file empty - stores a null in 1st position if doesn't load any characters
9053  { // may still have eof even if read a line (no CRLF at end), and
9054  // if so need to process it
9055  TimetableMessage(GiveMessages, "Timetable invalid - file empty");
9056  TTBLFile.close();
9057  delete TrainTimetableString;
9058  Utilities->CallLogPop(1611);
9059  return false;
9060  }
9061  AnsiString OneLine(TrainTimetableString);
9062  bool FinalCallFalse = false;
9063  while((Count == 0) && !ProcessOneTimetableLine(5, Count, OneLine, EndOfFile, FinalCallFalse, GiveMessages, CheckLocationsExistInRailway))
9064  // get rid of lines before the start time
9065  {
9066  // ProcessOneTimetableLine returns true for a valid start time, an EndOfFile &/or a blank entry
9067  TTBLFile.getline(TrainTimetableString, 10000, '\0');
9068  if(TTBLFile.eof() && (TrainTimetableString[0] == '\0'))
9069  // stores a null in 1st position if doesn't load any characters
9070  { // may still have eof even if read a line (no CRLF at end), and
9071  // if so need to process it
9072  TimetableMessage(GiveMessages, "Timetable invalid - unable to find a valid start time on its own line");
9073  TTBLFile.close();
9074  delete TrainTimetableString;
9075  Utilities->CallLogPop(772);
9076  return false;
9077  }
9078  OneLine = AnsiString(TrainTimetableString);
9079  }
9080  // here when have accepted the start time
9081  Count++; // increment past the start time
9082  while(!EndOfFile)
9083  {
9084  TTBLFile.getline(TrainTimetableString, 10000, '\0');
9085  // get next line after start time
9086  if(TTBLFile.eof() && (TrainTimetableString[0] == '\0'))
9087  // stores a null in 1st position if doesn't load any characters
9088  { // may still have eof even if read a line (no CRLF at end), and
9089  // if so need to process it
9090  EndOfFile = true;
9091  OneLine = "";
9092  }
9093  else
9094  OneLine = AnsiString(TrainTimetableString);
9095  if(OneLine.Length() > 9999)
9096  {
9097  TimetableMessage(GiveMessages, "Timetable contains a line that is too long - 10,000 or more characters!");
9098  TTBLFile.close();
9099  delete TrainTimetableString;
9100  Utilities->CallLogPop(789);
9101  return false;
9102  }
9103  bool FinalCallFalse = false;
9104  if(!ProcessOneTimetableLine(6, Count, OneLine, EndOfFile, FinalCallFalse, GiveMessages, CheckLocationsExistInRailway))
9105  // false for FinalCall - just checking at this stage
9106  {
9107  TTBLFile.close();
9108  delete TrainTimetableString;
9109  Utilities->CallLogPop(770);
9110  return false;
9111  }
9112  if(EndOfFile && (Count < 2))
9113  // Timetable must contain at least two relevant lines, one for start time and at least one train
9114  {
9115  TimetableMessage(GiveMessages, "Timetable has too few or no relevant entries - must have a start time on its own line and at least one train");
9116  TTBLFile.close();
9117  delete TrainTimetableString;
9118  Utilities->CallLogPop(771);
9119  return false;
9120  }
9121  Count++;
9122  }
9123  delete TrainTimetableString;
9124  TTBLFile.close();
9125  } // if(TTBLFile.is_open())
9126  else
9127  {
9128  TimetableMessage(GiveMessages, "Failed to open timetable file, make sure it's not open in another application");
9129  Utilities->CallLogPop(2154);
9130  return false;
9131  }
9132  Utilities->CallLogPop(753);
9133  return true;
9134 }
9135 
9136 // ---------------------------------------------------------------------------
9137 
9138 bool TTrainController::ProcessOneTimetableLine(int Caller, int Count, AnsiString OneLine, bool &EndOfFile, bool FinalCall, bool GiveMessages,
9139  bool CheckLocationsExistInRailway) // return true for success
9140 
9141 /* Format:
9142  Prior to start time, anything except a line beginning with a time [...leading spaces...] HH:MM is ignored - can be
9143  descriptive text or anything user wishes
9144  A time on its own line [HH:MM], with or without leading spaces, but with anything following it before the CR (which will
9145  be ignored) is taken as the timetable start time.
9146  Thereafter there must be text on every line in the timetable, as the first blank line (or end of file) will be taken as the end of the
9147  timetable. Text can follow the 'end of timetable' blank line if the user wishes.
9148  A line within the timetable beginning with '*', with or without leading spaces, is ignored. Such lines can add text
9149  within the timetable if required.
9150  Timetable entries consist of one line per headcode (i.e. per service, not necessarily per train, as one train can run several different
9151  services)
9152  Each line starts with HeadCode & full train information for a new train (Snt or Snt-sh), or, for a continuing service
9153  (Sfs, Sns, Sns-sh or Sns-fsh), can have (a) Headcode only or (b) HeadCode + Description, nothing else
9154 
9155  All leading & trailing spaces before & after a line or any entry in a line are stripped off - these can be included to make reading a
9156  text timetable file easier
9157 
9158  form:-
9159  HeadCode[;Description (plain text, no commas or semicolons)][;StartSpeed(kph); MaxRunningSpeed(kph); Mass(tonnes, prog converts to kg);
9160  MaxBrakeRate(tonnes force, prog converts to m/s/s); & gross power(kW, prog converts to power at rail in w)
9161  then multiple entries, separated by commas, of the form:-
9162 
9163  Format FormatType
9164  [W]HH:MM;Command (cdt) }TimeCmd }
9165  [W]HH:MM;Fer;set of allowable IDs }ExitRailway }
9166  [W]HH:MM;pas;Location }PassTime }
9167  [W]HH:MM;Snt;RearStartIdent FrontStartIdent }StartNew }
9168  [W]HH:MM;Command;HeadCode (Sfs Sns jbo fsp rsp Fns Fjo Frh-sh) }TimeCmdHeadCode }
9169  [W]HH:MM;F-nshs;non-repeating headcode }FNSNonRepeatToShuttle }
9170  [W]HH:MM;Sns-fsh;NonRepeatingShuttleLinkHeadCode }SNSNonRepeatFromShuttle } Train action entries
9171  [W]HH:MM;Snt-sh;RearStartIdent FrontStartIdent;FSH HeadCode }SNTShuttle }
9172  [W]HH:MM;Sns-sh;FSH HeadCode;F-nshs HeadCode (non-repeating) }SNSShuttle }
9173  [W]HH:MM;Fns-sh;Details }FSHNewService }
9174  [W]HH:MM;Location (arr & dep) }TimeLoc }
9175  [W]HH:MM;HH:MM;Location }TimeTimeLoc }
9176  Command (Frh only) }FinRemHere }
9177 
9178  R;mm;dd;nn. Repeat Repeat entry
9179 
9180  Two times represent arrival & departure, without any other events between (if arrival and departure times are the same
9181  then departure is 30 sec after arrival), single time represents (a) event time; (b) arrival time if train not already
9182  at location; or (c) departure time if train already at location (including train started at location either as a new
9183  train or as a continuation service train at that location). All lines must contain a start entry and a finish entry,
9184  the finish being the last unless there is a repeat entry. The repeat entry begins with 'R', then the incremental
9185  minutes, incremental train headcode last 2 digits, and number of repeats.
9186 
9187  Shuttle entries are where can loop back to an earlier Snt-sh or Sns-sh entry from a Frh-sh or Fns-sh (Finish Shuttle)
9188  entry. Here the shuttle start can have two entries, one from a set position (Snt-sh, must be located) or from a F-nshs
9189  (Sns-sh) - with NO repeat from this source, and from a Fxx-sh, with repeats. After all shuttle repeats Frh-sh remains
9190  where it is, and Fns-sh links to a new service (via an Sns entry), but there must be no repeats in this new service
9191  (it's for a shuttle train to return to depot at end of services)
9192 
9193  Command/Location & details are as follows:-
9194 
9195  Although headcodes can be duplicated, all joins, splits, new services etc give other headcode from both trains' povs, and
9196  these have to match once only, i.e. if 2E44 splits to 2E45 then it can't split to 2E45 anywhere else, and 2E45 must give
9197  2E44 in its Sfs entry. All these are checked.
9198  ***add note re shuttles & their use of otherheadcodes + non-repeating headcodes***
9199 
9200  Start commands:-
9201  Snt (StartNew) = Start New Train, i.e. create new train, details = rearstartident, space, frontstartident (can't confuse
9202  with loc as a start entry can't have a location as details)
9203  Sfs (TimeCmdHeadCode) = Start From Split, create a new train that has split from another train (& listed in other train's
9204  timetable line), details = other headcode - (can't confuse with loc as start can't be a loc)
9205  Sns (TimeCmdHeadCode) = Start, headcode change from earlier service - no need to create train as already exists, it just
9206  changes its relevant information, details = old headcode (can't confuse with loc as start can't be a loc)
9207  Snt-sh (SNTShuttle) = Start New Train, i.e. create new train, details = rearstartident, space, frontstartident (can't
9208  confuse with loc as start can't be a loc) then the Fsh-XX service headcode (OtherHeadCode can't be same headcode)
9209  Sns-sh (SNSShuttle) = Start, headcode change from earlier service - no need to create train as already exists, it just
9210  changes its relevant information, details = the FSH-XX service headcode (OtherHeadCode, can't be same headcode)
9211  followed by the non-repeating F-nshs headcode (NonRepeatingShuttleLinkHeadCode)
9212  Sns-fsh (SNSNonRepeatFromShuttle) = Start as a non-repeating service from a shuttle service that has finished all its
9213  repeats, details = NonRepeatingShuttleLinkHeadCode for the corresponding shuttle Fns-sh service
9214 
9215  Intermediate commands:-
9216  Time - Location (TimeLoc), can be arrival or departure depending on context
9217  Time Time location (TimeTimeLoc), arrival and departure
9218  Location Name (exactly as used in the railway) in TimeLoc & TimeTimeLoc means that the train is required to stop at the location
9219  pas (PassTime), Time;pas;Location
9220  jbo (TimeCmdHeadCode) = Joined By Other = joined by other train, details = new headcode (await other train - may be delayed). Note that the
9221  joining train's finish details must correspond or the file check will fail
9222  fsp (TimeCmdHeadCode) = Front Split = a new train splits away from front of this train, both trains in same direction, details = new headcode (create
9223  new train - that train's starting information must correspond)
9224  rsp (TimeCmdHeadCode) = Rear Split = a new train splits away from rear of this train, both trains in same direction, details = new headcode (create
9225  new train - that train's starting information must correspond)
9226  cdt (TimeCmd) = Change Direction of Train = change direction, no details needed & no train creation
9227 
9228  Finish commands:-
9229  Fns (TimeCmdHeadCode) = Finish New Service = finish, form new service in same direction, details = new headcode (no train
9230  creation)
9231  F-nshs (FNSShuttle) = Finish New Service (Shuttle) = finish, form new shuttle service in same direction, details =
9232  shuttle headcode (no train creation)
9233  Fjo (TimeCmdHeadCode) = Finish Join Other = finish, join other train (which must be on an adjacent element, either end -
9234  may have to wait for it), details = new headcode (delete train)
9235  Frh (FinRemHere) = Finish Remain Here = stay here indefinitely, no details & no time needed
9236  Fer (ExitRailway) = Finish, exit railway (i.e at a continuation) - details = set of allowable exit IDs
9237  Frh-sh (TimeCmdHeadCode) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done remain
9238  here
9239  Fns-sh (FSHNewService) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done form new
9240  service via Sns-fsh using the NonRepeatingShuttleLinkHeadCode
9241 
9242  Repeat:-
9243  R;mm;dd;nn (Repeat) where mm = minute increment, dd = 2nd 2 headcode digit increment & nn = no. of repeats (no max as can duplicate
9244  headcodes - it is up to user to avoid duplicates if he/she wishes to.
9245 
9246  Checks carried out with error messages in this function:-
9247  At least one comma in a service line (it's based on a .csv file)
9248  No entries following train information;
9249  At least one comma in remainder after train information (i.e at least a start and a finish entry);
9250  SplitEntry returns false in an intermediate entry - message repeats the entry for information;
9251  First entry not a start entry;
9252  Train information incomplete before a start entry;
9253  Entry follows a finish entry but doesn't begin with 'R';
9254  SplitEntry returns false in a finish entry - message repeats the entry for information;
9255  Last action entry isn't a finish entry.
9256 
9257  Function returns false with no message if:-
9258  Timetable start time invalid (no message is given for an invalid time as the line is assumed to be an irrelevant line; if no start
9259  time is found at all then an error message is given in the calling function);
9260  SplitTrainInfo returns false (message given in called function);
9261  SplitRepeat returns false (message given in called function).
9262 */ {
9263  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ProcessOneTimetableLine," + AnsiString(Count) + "," + OneLine + "," +
9264  AnsiString((short)FinalCall) + "," + AnsiString((short)CheckLocationsExistInRailway));
9265  TTrainDataEntry TempTrainDataEntry;
9266 
9267  EndOfFile = false;
9268  StripSpaces(0, OneLine);
9269  // strip both leading and trailing spaces at ends of line and spaces before and after all commas and
9270  // semicolons within the line
9271  ServiceReference = "";
9272  if(OneLine != "")
9273  {
9274  if(OneLine[1] != '*')
9275  {
9276  int SCPos = OneLine.Pos(';');
9277  if(SCPos == 0)
9278  ServiceReference = OneLine.SubString(1, 8);
9279  else
9280  ServiceReference = OneLine.SubString(1, (SCPos - 1));
9281  }
9282  }
9283  bool AllCommas = true;
9284 
9285  for(int x = 1; x < OneLine.Length() + 1; x++) // check for nothing but commas (may be all commas if created from Excel) or a blank line
9286  {
9287  if(OneLine[x] != ',')
9288  AllCommas = false;
9289  }
9290  if(AllCommas || (OneLine == ""))
9291  {
9292  if(Count > 0)
9293  {
9294  EndOfFile = true;
9295  // returns true for a blank line - treated as end of file
9296  Utilities->CallLogPop(1018);
9297  return true;
9298  }
9299  else // count == 0 so not yet found a start time, no message to be given
9300  {
9301  Utilities->CallLogPop(754);
9302  return false;
9303  }
9304  }
9305  AnsiString First = "", Second = "", Third = "", Fourth = "";
9306  int RearStartOrRepeatMins = 0, FrontStartOrRepeatDigits = 0, NumberOfRepeats = 0;
9307  TDateTime EventTime(0), ArrivalTime(0), DepartureTime(0);
9308  TDateTime StartTime(0);
9309  TExitList ExitList;
9310  bool Warning = false;
9311 
9312  if(Count == 0) // no start time found yet
9313  {
9314 /* dropped at v0.6b
9315  AnyHeadCodeValid = false;
9316  if(OneLine.SubString(6,5) == ";0000")
9317  {
9318  AnyHeadCodeValid = true;
9319  }
9320 */
9321  if(!CheckTimeValidity(0, OneLine, StartTime))
9322  {
9323  // no message is given for an invalid time as it's assumed to be an irrelevant line
9324  // if no start time is found at all then an error message is given in the calling function
9325  // AnyHeadCodeValid = false;
9326  Utilities->CallLogPop(755);
9327  return false;
9328  }
9329  if(FinalCall) // here if start time valid
9330  {
9331  TTClockTime = StartTime;
9332  TimetableStartTime = StartTime;
9333  }
9334  }
9335  else
9336  {
9337  AnsiString TrainInfoStr = "", HeadCode = "", Description = "";
9338  int StartSpeed = 0, MaxRunningSpeed = 0, Mass = 0;
9339  double MaxBrakeRate = 0;
9340  double PowerAtRail = 0;
9341  int SignallerSpeed = 0;
9342  if(OneLine[1] == '*')
9343  {
9344  Utilities->CallLogPop(1581);
9345  return true;
9346  // ignore any line beginning with '*' but return true as there is no error
9347  }
9348  int Pos = OneLine.Pos(',');
9349  if(Pos == 0)
9350  {
9351  int SubStringLength = 20;
9352  if(OneLine.Length() < 20)
9353  SubStringLength = OneLine.Length();
9354  TimetableMessage(GiveMessages, "Error in timetable - entry incomplete: see '" + OneLine.SubString(1, SubStringLength) + "'....");
9355  Utilities->CallLogPop(766);
9356  return false;
9357  }
9358  TrainInfoStr = OneLine.SubString(1, Pos - 1);
9359  if(!SplitTrainInfo(0, TrainInfoStr, HeadCode, Description, StartSpeed, MaxRunningSpeed, Mass, MaxBrakeRate, PowerAtRail, SignallerSpeed,
9360  GiveMessages)) // error messages given in SplitTrainInfo
9361  {
9362  Utilities->CallLogPop(773);
9363  return false;
9364  }
9365  if(FinalCall)
9366  {
9367  // store Train info - conversions done in SplitTrainInfo
9368  // only headcode mandatory for continuing services
9369  TempTrainDataEntry.HeadCode = HeadCode;
9370  TempTrainDataEntry.ServiceReference = HeadCode;
9371  TempTrainDataEntry.Description = Description;
9372  TempTrainDataEntry.StartSpeed = StartSpeed;
9373  TempTrainDataEntry.Mass = Mass;
9374  TempTrainDataEntry.MaxRunningSpeed = MaxRunningSpeed;
9375  TempTrainDataEntry.MaxBrakeRate = MaxBrakeRate;
9376  TempTrainDataEntry.PowerAtRail = PowerAtRail;
9377  TempTrainDataEntry.SignallerSpeed = SignallerSpeed;
9378  TTrainOperatingData TempTrainOperatingData;
9379  TempTrainDataEntry.TrainOperatingDataVector.push_back(TempTrainOperatingData); // push empty vector for now
9380  }
9381  AnsiString NewRemainder = OneLine.SubString(Pos + 1, OneLine.Length() - Pos);
9382  // now left with series of entries for this train, but there may be a string of commas at the end of the line if created by Excel
9383  // so strip them off
9384  while(NewRemainder[NewRemainder.Length()] == ',')
9385  {
9386  if(NewRemainder.Length() > 1)
9387  {
9388  NewRemainder = NewRemainder.SubString(1, NewRemainder.Length() - 1);
9389  }
9390  else
9391  {
9392  NewRemainder = "";
9393  break;
9394  }
9395  }
9396  // check if zero length & fail if so
9397  if(NewRemainder == "")
9398  {
9399  TimetableMessage(GiveMessages, "Error in timetable - no events following train: '" + OneLine + "'");
9400  Utilities->CallLogPop(769);
9401  return false;
9402  }
9403  // now have one more entry than there are commas
9404  int CommaCount = 0;
9405  for(int x = 1; x < NewRemainder.Length() + 1; x++)
9406  {
9407  if(NewRemainder[x] == ',')
9408  CommaCount++;
9409  } // must have at least 1 comma, for start & finish entries, unless train is entered under signaller control
9410  if(CommaCount == 0)
9411  {
9412  if((NewRemainder.SubString(7, 3) != "Snt") || (NewRemainder[NewRemainder.Length()] != 'S'))
9413  {
9414  int SubStringLength = 20;
9415  if(OneLine.Length() < 20)
9416  SubStringLength = OneLine.Length();
9417  TimetableMessage(GiveMessages,
9418  "Error in timetable - must have at least a start and a finish event for a train that is not started under signaller control - see line beginning: '" +
9419  OneLine.SubString(1, SubStringLength) + "'....");
9420  Utilities->CallLogPop(783);
9421  return false;
9422  }
9423  }
9424  AnsiString OneEntry = "";
9425  TTimetableFormatType FormatType;
9426  TTimetableSequenceType SequenceType;
9427  TTimetableLocationType LocationType;
9428  TTimetableShuttleLinkType ShuttleLinkType;
9429  bool FinishFlag = false;
9430  for(int x = 0; x < CommaCount + 1; x++)
9431  {
9432  if((CommaCount == 0) || (x < CommaCount))
9433  // i.e. train entered under signaller control with no repeats, or entry is not the last,
9434  // in which case there's a comma & finish element or repeat still to come this entry could
9435  // be a finish but can't be a repeat
9436  {
9437  if(CommaCount == 0)
9438  {
9439  OneEntry = NewRemainder;
9440  NewRemainder = "";
9441  }
9442  else
9443  {
9444  Pos = NewRemainder.Pos(',');
9445  OneEntry = NewRemainder.SubString(1, Pos - 1);
9446  NewRemainder = NewRemainder.SubString(Pos + 1, NewRemainder.Length() - Pos);
9447  }
9448  First = "";
9449  Second = "";
9450  Third = "";
9451  Fourth = "";
9452  RearStartOrRepeatMins = 0;
9453  FrontStartOrRepeatDigits = 0;
9454  NumberOfRepeats = 0;
9455  if(!SplitEntry(0, OneEntry, GiveMessages, CheckLocationsExistInRailway, First, Second, Third, Fourth, RearStartOrRepeatMins,
9456  FrontStartOrRepeatDigits, FormatType, LocationType, SequenceType, ShuttleLinkType, ExitList, Warning))
9457  {
9458  TimetableMessage(GiveMessages, "Error in timetable - Event: '" + OneEntry + "'");
9459  Utilities->CallLogPop(756);
9460  return false;
9461  }
9462  // check if warning for Frh or Fjo & reject
9463  if(Warning && (Second == "Frh"))
9464  {
9465  TimetableMessage(GiveMessages, "Error in line - '" + OneEntry + "': warnings cannot be given for 'Frh' events");
9466  Utilities->CallLogPop(1793);
9467  return false;
9468  }
9469  if(Warning && (Second == "Fjo"))
9470  {
9471  TimetableMessage(GiveMessages, "Error in line - '" + OneEntry +
9472  "': warnings cannot be given for 'Fjo' events, for a train join warning add a 'W' prefix to the 'jbo' event");
9473  Utilities->CallLogPop(1794);
9474  return false;
9475  }
9476  if(x == 0) // should be start event
9477  {
9478  if(SequenceType != Start)
9479  {
9480  TimetableMessage(GiveMessages, "Error in timetable - First event not a start event: '" + OneEntry + "'");
9481  Utilities->CallLogPop(784);
9482  return false;
9483  }
9484  if((Second == "Snt") && (Fourth == 'S') && (NewRemainder != ""))
9485  {
9486  if(NewRemainder[1] != 'R')
9487  {
9488  TimetableMessage(GiveMessages,
9489  "Error in timetable - the only event that can follow a train created under signaller control is a repeat, see '" +
9490  OneEntry + "'");
9491  Utilities->CallLogPop(787);
9492  return false;
9493  }
9494  }
9495  if((Second == "Snt") || (Second == "Snt-sh"))
9496  // need full train information including non-default values for at least HeadCode, Description,
9497  // MaxRunningSpeed, Mass, MaxBrakeRate, & PowerAtRail
9498  {
9499  if((HeadCode == "") || (Description == "") || (MaxRunningSpeed == 0) || (Mass == 0) || (MaxBrakeRate == 0)) // ||
9500  // (PowerAtRail == 0)) allowed 0 for power at v2.4.0
9501  {
9502  TimetableMessage(GiveMessages, "Error in timetable - train information incomplete before 'Snt' or 'Snt-sh' start event: '" +
9503  OneEntry + "'");
9504  Utilities->CallLogPop(1783);
9505  return false;
9506  }
9507  }
9508  if((Second == "Sfs") || (Second == "Sns") || (Second == "Sns-sh") || (Second == "Sns-fsh"))
9509  // service continuation - need at least non-default value for HeadCode
9510  {
9511  if(HeadCode == "")
9512  {
9513  TimetableMessage(GiveMessages, "Error in timetable - headcode missing before 'Sfs', 'Sns', 'Sns-sh' or 'Sns-fsh' start event: '" +
9514  OneEntry + "'");
9515  Utilities->CallLogPop(788);
9516  return false;
9517  }
9518  if((StartSpeed != 0) || (MaxRunningSpeed != 0) || (Mass != 0) || (MaxBrakeRate != 0) || (PowerAtRail != 0))
9519  {
9520  TimetableMessage(GiveMessages,
9521  "Error in timetable - information additional to a headcode & optional description given before 'Sfs', 'Sns', 'Sns-sh' or 'Sns-fsh' start event: '" +
9522  OneEntry + "'");
9523  Utilities->CallLogPop(843);
9524  return false;
9525  }
9526  }
9527  }
9528  if(SequenceType == Finish)
9529  {
9530  FinishFlag = true;
9531  // marker for only permitted additional entry being a repeat, only needed if the
9532  // finish entry is not the last entry
9533  }
9534  if(FinalCall)
9535  {
9536  // interpret & add to ActionVector
9537  TDateTime TempTime;
9538  TActionVectorEntry ActionVectorEntry;
9539  ActionVectorEntry.FormatType = FormatType;
9540  ActionVectorEntry.LocationType = LocationType;
9541  ActionVectorEntry.SequenceType = SequenceType;
9542  ActionVectorEntry.ShuttleLinkType = ShuttleLinkType;
9543  ActionVectorEntry.Warning = Warning;
9544  if(FormatType == TimeLoc)
9545  {
9546  if(CheckTimeValidity(1, First, ActionVectorEntry.EventTime))
9547  {;
9548  } // these will all be true as final call
9549  ActionVectorEntry.LocationName = Second;
9550  }
9551  else if(FormatType == PassTime) // new
9552  {
9553  if(CheckTimeValidity(17, First, ActionVectorEntry.EventTime))
9554  {;
9555  }
9556  ActionVectorEntry.Command = Second;
9557  ActionVectorEntry.LocationName = Third;
9558  }
9559  else if(FormatType == TimeTimeLoc)
9560  {
9561  if(CheckTimeValidity(2, First, ActionVectorEntry.ArrivalTime))
9562  {;
9563  }
9564  if(CheckTimeValidity(3, Second, ActionVectorEntry.DepartureTime))
9565  {;
9566  }
9567  ActionVectorEntry.LocationName = Third;
9568  }
9569  else if(FormatType == TimeCmd)
9570  {
9571  if(CheckTimeValidity(4, First, ActionVectorEntry.EventTime))
9572  {;
9573  }
9574  ActionVectorEntry.Command = Second;
9575  }
9576  else if(FormatType == ExitRailway)
9577  {
9578  if(CheckTimeValidity(18, First, ActionVectorEntry.EventTime))
9579  {;
9580  }
9581  ActionVectorEntry.Command = Second;
9582  ActionVectorEntry.ExitList = ExitList;
9583  }
9584  else if(FormatType == StartNew)
9585  {
9586  if(CheckTimeValidity(5, First, ActionVectorEntry.EventTime))
9587  {;
9588  }
9589  ActionVectorEntry.Command = Second;
9590  ActionVectorEntry.RearStartOrRepeatMins = RearStartOrRepeatMins;
9591  ActionVectorEntry.FrontStartOrRepeatDigits = FrontStartOrRepeatDigits;
9592  if(Fourth == 'S')
9593  ActionVectorEntry.SignallerControl = true;
9594  }
9595  else if(FormatType == SNTShuttle)
9596  {
9597  if(CheckTimeValidity(6, First, ActionVectorEntry.EventTime))
9598  {;
9599  }
9600  ActionVectorEntry.Command = Second;
9601  ActionVectorEntry.RearStartOrRepeatMins = RearStartOrRepeatMins;
9602  ActionVectorEntry.FrontStartOrRepeatDigits = FrontStartOrRepeatDigits;
9603  ActionVectorEntry.OtherHeadCode = Fourth;
9604  }
9605  else if(FormatType == SNSShuttle)
9606  {
9607  if(CheckTimeValidity(7, First, ActionVectorEntry.EventTime))
9608  {;
9609  }
9610  ActionVectorEntry.Command = Second;
9611  ActionVectorEntry.OtherHeadCode = Third;
9612  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Fourth;
9613  }
9614  else if(FormatType == TimeCmdHeadCode)
9615  {
9616  if(CheckTimeValidity(8, First, ActionVectorEntry.EventTime))
9617  {;
9618  }
9619  ActionVectorEntry.Command = Second;
9620  ActionVectorEntry.OtherHeadCode = Third;
9621  }
9622  else if((FormatType == FNSNonRepeatToShuttle) || (FormatType == SNSNonRepeatFromShuttle))
9623  {
9624  if(CheckTimeValidity(9, First, ActionVectorEntry.EventTime))
9625  {;
9626  }
9627  ActionVectorEntry.Command = Second;
9628  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Third;
9629  }
9630  else if(FormatType == FSHNewService)
9631  {
9632  if(CheckTimeValidity(10, First, ActionVectorEntry.EventTime))
9633  {;
9634  }
9635  ActionVectorEntry.Command = Second;
9636  ActionVectorEntry.OtherHeadCode = Third;
9637  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Fourth;
9638  }
9639  else if(FormatType == FinRemHere)
9640  {
9641  ActionVectorEntry.Command = Second;
9642  }
9643  TempTrainDataEntry.ActionVector.push_back(ActionVectorEntry);
9644  }
9645  }
9646  else // last entry, & not entered under signaller control with no repeats, i.e. could be finish or repeat
9647  {
9648  OneEntry = NewRemainder;
9649  First = "";
9650  Second = "";
9651  Third = "";
9652  Fourth = "";
9653  RearStartOrRepeatMins = 0;
9654  FrontStartOrRepeatDigits = 0;
9655  NumberOfRepeats = 0;
9656  if((FinishFlag) && (OneEntry[1] != 'R'))
9657  // already had a finish entry
9658  {
9659  TimetableMessage(GiveMessages, "Error in timetable - Last event = '" + OneEntry + "'. An earlier finish event has been found with something other than a repeat following it - only a repeat can follow a finish event.");
9660  Utilities->CallLogPop(79);
9661  return false;
9662  }
9663  if(OneEntry[1] != 'R') // must be finish
9664  {
9665  if(!SplitEntry(1, OneEntry, GiveMessages, CheckLocationsExistInRailway, First, Second, Third, Fourth, RearStartOrRepeatMins,
9666  FrontStartOrRepeatDigits, FormatType, LocationType, SequenceType, ShuttleLinkType, ExitList, Warning))
9667  {
9668  TimetableMessage(GiveMessages, "Error in timetable - Event: '" + OneEntry + "'");
9669  Utilities->CallLogPop(757);
9670  return false;
9671  }
9672  if(SequenceType != Finish)
9673  {
9674  TimetableMessage(GiveMessages, "Error in timetable - last event should be a finish: '" + OneEntry + "'");
9675  Utilities->CallLogPop(785);
9676  return false;
9677  }
9678  if(FinalCall)
9679  {
9680  // interpret & add to ActionVector
9681  TDateTime TempTime;
9682  TActionVectorEntry ActionVectorEntry;
9683  ActionVectorEntry.FormatType = FormatType;
9684  ActionVectorEntry.LocationType = LocationType;
9685  ActionVectorEntry.SequenceType = SequenceType;
9686  ActionVectorEntry.ShuttleLinkType = ShuttleLinkType;
9687  ActionVectorEntry.Warning = Warning;
9688  if(FormatType == TimeCmd)
9689  {
9690  if(CheckTimeValidity(11, First, ActionVectorEntry.EventTime))
9691  {;
9692  }
9693  ActionVectorEntry.Command = Second;
9694  }
9695  else if(FormatType == TimeCmdHeadCode)
9696  {
9697  if(CheckTimeValidity(12, First, ActionVectorEntry.EventTime))
9698  {;
9699  }
9700  ActionVectorEntry.Command = Second;
9701  ActionVectorEntry.OtherHeadCode = Third;
9702  }
9703  else if(FormatType == FNSNonRepeatToShuttle)
9704  {
9705  if(CheckTimeValidity(13, First, ActionVectorEntry.EventTime))
9706  {;
9707  }
9708  ActionVectorEntry.Command = Second;
9709  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Third;
9710  }
9711  else if(FormatType == FSHNewService)
9712  {
9713  if(CheckTimeValidity(14, First, ActionVectorEntry.EventTime))
9714  {;
9715  }
9716  ActionVectorEntry.Command = Second;
9717  ActionVectorEntry.OtherHeadCode = Third;
9718  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Fourth;
9719  }
9720  else if(FormatType == ExitRailway)
9721  {
9722  if(CheckTimeValidity(19, First, ActionVectorEntry.EventTime))
9723  {;
9724  }
9725  ActionVectorEntry.Command = Second;
9726  ActionVectorEntry.ExitList = ExitList;
9727  }
9728  else if(FormatType == FinRemHere)
9729  {
9730  ActionVectorEntry.Command = Second;
9731  }
9732  TempTrainDataEntry.ActionVector.push_back(ActionVectorEntry);
9733  }
9734  }
9735  else // repeat
9736  {
9737  if(!SplitRepeat(0, OneEntry, RearStartOrRepeatMins, FrontStartOrRepeatDigits, NumberOfRepeats, GiveMessages))
9738  {
9739  Utilities->CallLogPop(786);
9740  // error messages given in SplitRepeat
9741  return false;
9742  }
9743  if(FinalCall)
9744  {
9745  TActionVectorEntry ActionVectorEntry;
9746  ActionVectorEntry.FormatType = Repeat;
9747  ActionVectorEntry.LocationType = LocTypeForRepeatEntry;
9748  ActionVectorEntry.SequenceType = SequTypeForRepeatEntry;
9749  ActionVectorEntry.ShuttleLinkType = ShuttleLinkTypeForRepeatEntry;
9750  ActionVectorEntry.RearStartOrRepeatMins = RearStartOrRepeatMins;
9751  ActionVectorEntry.FrontStartOrRepeatDigits = FrontStartOrRepeatDigits;
9752  ActionVectorEntry.NumberOfRepeats = NumberOfRepeats;
9753  TempTrainDataEntry.ActionVector.push_back(ActionVectorEntry);
9754  }
9755  }
9756  }
9757  }
9758  if(FinalCall)
9759  {
9760  TrainDataVector.push_back(TempTrainDataEntry);
9761  }
9762  }
9763  Utilities->CallLogPop(80);
9764  return true;
9765 }
9766 
9767 // ---------------------------------------------------------------------------
9768 
9769 bool TTrainController::Last2CharactersBothDigits(int Caller, AnsiString HeadCode)
9770 {
9771  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",Last2CharactersBothDigits," + HeadCode);
9772  if((HeadCode[HeadCode.Length() - 1] < '0') || (HeadCode[HeadCode.Length() - 1] > '9'))
9773  {
9774  Utilities->CallLogPop(1890);
9775  return false;
9776  }
9777  if((HeadCode[HeadCode.Length()] < '0') || (HeadCode[HeadCode.Length()] > '9'))
9778  {
9779  Utilities->CallLogPop(1891);
9780  return false;
9781  }
9782  Utilities->CallLogPop(1892);
9783  return true;
9784 }
9785 
9786 // ---------------------------------------------------------------------------
9787 
9788 bool TTrainController::CheckTimeValidity(int Caller, AnsiString TimeStr, TDateTime &Time)
9789  // 1st 5 chars must be HH:MM, anything else will be ignored
9790 {
9791  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckTimeValidity," + TimeStr);
9792  if(TimeStr.Length() < 5)
9793  {
9794  Utilities->CallLogPop(926);
9795  return false;
9796  }
9797  if((TimeStr[1] < '0') || (TimeStr[1] > '9'))
9798  {
9799  Utilities->CallLogPop(927);
9800  return false;
9801  }
9802  if((TimeStr[2] < '0') || (TimeStr[2] > '9'))
9803  {
9804  Utilities->CallLogPop(928);
9805  return false;
9806  }
9807  if(TimeStr[3] != ':')
9808  {
9809  Utilities->CallLogPop(929);
9810  return false;
9811  }
9812  if((TimeStr[4] < '0') || (TimeStr[4] > '5'))
9813  {
9814  Utilities->CallLogPop(930);
9815  return false;
9816  }
9817  if((TimeStr[5] < '0') || (TimeStr[5] > '9'))
9818  {
9819  Utilities->CallLogPop(931);
9820  return false;
9821  }
9822  while(TimeStr.Length() > 5)
9823  TimeStr = TimeStr.SubString(1, TimeStr.Length() - 1);
9824  double WholeHours = (AnsiString(TimeStr[1]) + AnsiString(TimeStr[2])).ToDouble();
9825  double FracHour = ((AnsiString(TimeStr[4]) + AnsiString(TimeStr[5])).ToDouble()) / 60.0;
9826 
9827  if((WholeHours + FracHour) >= 95.98334)
9828  {
9829  Utilities->CallLogPop(1817);
9830  return false; // > 95h 59m
9831  }
9832  Time = TDateTime((WholeHours + FracHour) / 24);
9833  Utilities->CallLogPop(932);
9834  return true;
9835 }
9836 
9837 // ---------------------------------------------------------------------------
9838 
9839 bool TTrainController::SplitEntry(int Caller, AnsiString OneEntry, bool GiveMessages, bool CheckLocationsExistInRailway, AnsiString &First, AnsiString &Second,
9840  AnsiString &Third, AnsiString &Fourth, int &RearStartOrRepeatMins, int &FrontStartOrRepeatDigits, TTimetableFormatType &FormatType,
9841  TTimetableLocationType &LocationType, TTimetableSequenceType &SequenceType, TTimetableShuttleLinkType &ShuttleLinkType, TExitList &ExitList, bool &Warning)
9842 /* This is a train action entry from a single line of the timetable, i.e. not train information and not a repeat entry.
9843  Return false for failure.
9844  See description above under ProcessOneTimetableLinefor details of train action entries
9845  NB all types set except LocationType for Sns as may be located or not
9846 */ {
9847  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SplitEntry," + OneEntry);
9848  Warning = false;
9849  TDateTime TempTime;
9850 
9851  if(OneEntry.Length() > 0)
9852  {
9853  if(OneEntry[1] == 'W') // warning
9854  {
9855  Warning = true;
9856  OneEntry = OneEntry.SubString(2, OneEntry.Length() - 1);
9857  // strip it off
9858  }
9859  }
9860  if(OneEntry == "Frh")
9861  {
9862  FormatType = FinRemHere;
9863  SequenceType = Finish;
9864  LocationType = AtLocation;
9865  ShuttleLinkType = NotAShuttleLink;
9866  Second = "Frh";
9867  Utilities->CallLogPop(1016);
9868  return true;
9869  }
9870  if(OneEntry.Length() < 7)
9871  {
9872  Utilities->CallLogPop(907);
9873  return false; // 'HH:MM;' + at least a one-letter location name
9874  }
9875  int Pos = OneEntry.Pos(';'); // first segment delimiter
9876 
9877  if(Pos != 6)
9878  {
9879  Utilities->CallLogPop(908);
9880  return false;
9881  // no delimiter or delimiter not in position 6, has to be a time so fail
9882  }
9883  First = OneEntry.SubString(1, 5); // has to be a time
9884  if(!CheckTimeValidity(16, First, TempTime))
9885  {
9886  Utilities->CallLogPop(909);
9887  return false;
9888  }
9889  AnsiString Remainder = OneEntry.SubString(Pos + 1, OneEntry.Length() - Pos);
9890 
9891  if((Remainder[1] >= '0') && (Remainder[1] <= '9'))
9892  // next segment is a time so this is a TimeTimeLoc & 3rd seg has to be a location to be valid
9893  {
9894  if(Remainder.Length() < 7)
9895  {
9896  Utilities->CallLogPop(910);
9897  return false; // 'HH:MM;' + at least a one-letter location name
9898  }
9899  Pos = Remainder.Pos(';'); // second segment delimiter
9900  if(Pos == 0)
9901  {
9902  Utilities->CallLogPop(911);
9903  return false;
9904  // no delimiter, has to be one between departure time & location
9905  }
9906  Second = Remainder.SubString(1, 5); // has to be a time
9907  if(!CheckTimeValidity(15, Second, TempTime))
9908  {
9909  Utilities->CallLogPop(912);
9910  return false;
9911  }
9912  Third = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
9913  if(!CheckLocationValidity(0, Third, GiveMessages, CheckLocationsExistInRailway))
9914  {
9915  Utilities->CallLogPop(913);
9916  return false;
9917  }
9918  FormatType = TimeTimeLoc;
9919  SequenceType = Intermediate;
9920  LocationType = AtLocation;
9921  ShuttleLinkType = NotAShuttleLink;
9922  Utilities->CallLogPop(914);
9923  return true;
9924  }
9925  Pos = Remainder.Pos(';'); // second segment delimiter
9926  if(Pos == 0) // no third segment so second must be a location, or cdt
9927  {
9928  Second = Remainder;
9929  if(Second == "cdt")
9930  {
9931  FormatType = TimeCmd;
9932  ShuttleLinkType = NotAShuttleLink;
9933  LocationType = AtLocation;
9934  SequenceType = Intermediate;
9935  Utilities->CallLogPop(915);
9936  return true;
9937  }
9938  if(!CheckLocationValidity(1, Second, GiveMessages, CheckLocationsExistInRailway))
9939  {
9940  Utilities->CallLogPop(916);
9941  return false;
9942  }
9943  else
9944  {
9945  FormatType = TimeLoc;
9946  LocationType = AtLocation;
9947  SequenceType = Intermediate;
9948  ShuttleLinkType = NotAShuttleLink;
9949  Utilities->CallLogPop(917);
9950  return true;
9951  }
9952  }
9953  // here if second segment is a command, with a third & maybe fourth segments as details
9954  if((Pos != 4) && (Pos != 7) && (Pos != 8))
9955  {
9956  Utilities->CallLogPop(918);
9957  return false;
9958  // no third segement or not in position 4 or 7, & should be since all commands are 3, 6 or 7 letters
9959  }
9960  Second = Remainder.SubString(1, Pos - 1); // command
9961 
9962  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
9963  // details
9964  Pos = Remainder.Pos(';'); // third segment delimiter
9965  if(Pos == 0)
9966  Third = Remainder;
9967  else
9968  {
9969  Third = Remainder.SubString(1, Pos - 1);
9970  Fourth = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
9971  }
9972  if((Second == "Snt") || (Second == "Snt-sh"))
9973  // third has to be 2 element idents with a space between
9974  {
9975  int SpacePos = Third.Pos(' ');
9976  if(SpacePos == 0)
9977  {
9978  Utilities->CallLogPop(919);
9979  return false; // no space
9980  }
9981  AnsiString RearStartStr = Third.SubString(1, SpacePos - 1);
9982  AnsiString FrontStartStr = Third.SubString(SpacePos + 1, Third.Length() - SpacePos);
9983  // int RearPosition=0, FrontPosition=0, RearExitPos=0;
9984  if(CheckLocationsExistInRailway)
9985  {
9986  if(!CheckStartPositionValidity(0, RearStartStr, FrontStartStr, GiveMessages))
9987  {
9988  Utilities->CallLogPop(920);
9989  return false;
9990  }
9991  RearStartOrRepeatMins = Track->GetTrackVectorPositionFromString(3, RearStartStr, GiveMessages);
9992  FrontStartOrRepeatDigits = Track->GetTrackVectorPositionFromString(4, FrontStartStr, GiveMessages);
9993  }
9994  if(Second == "Snt")
9995  {
9996  FormatType = StartNew;
9997  SequenceType = Start;
9998  LocationType = NoLocation;
9999  // can't be set until know whether located or not - done in SecondPassActions
10000  ShuttleLinkType = NotAShuttleLink;
10001  }
10002  else // Snt-sh
10003  {
10004  FormatType = SNTShuttle;
10005  LocationType = AtLocation;
10006  SequenceType = Start;
10007  ShuttleLinkType = ShuttleLink;
10008  if(!CheckHeadCodeValidity(0, GiveMessages, Fourth))
10009  {
10010  Utilities->CallLogPop(1038);
10011  return false;
10012  }
10013  }
10014  Utilities->CallLogPop(921);
10015  return true;
10016  }
10017 
10018  if(Second == "Sns-sh") // third & fourth have to be headcodes
10019  {
10020  FormatType = SNSShuttle;
10021  LocationType = AtLocation;
10022  SequenceType = Start;
10023  ShuttleLinkType = ShuttleLink;
10024  if(!CheckHeadCodeValidity(1, GiveMessages, Third))
10025  {
10026  Utilities->CallLogPop(1039);
10027  return false;
10028  }
10029  if(!CheckHeadCodeValidity(2, GiveMessages, Fourth))
10030  {
10031  Utilities->CallLogPop(1040);
10032  return false;
10033  }
10034  Utilities->CallLogPop(1041);
10035  return true;
10036  }
10037 
10038  if(Second == "F-nshs")
10039  {
10040  FormatType = FNSNonRepeatToShuttle;
10041  LocationType = AtLocation;
10042  SequenceType = Finish;
10043  ShuttleLinkType = ShuttleLink;
10044  if(!CheckHeadCodeValidity(3, GiveMessages, Third))
10045  {
10046  Utilities->CallLogPop(1047);
10047  return false;
10048  }
10049  Utilities->CallLogPop(1048);
10050  return true;
10051  }
10052 
10053  if(Second == "Sns-fsh")
10054  {
10055  FormatType = SNSNonRepeatFromShuttle;
10056  LocationType = AtLocation;
10057  SequenceType = Start;
10058  ShuttleLinkType = ShuttleLink;
10059  if(!CheckHeadCodeValidity(4, GiveMessages, Third))
10060  {
10061  Utilities->CallLogPop(1098);
10062  return false;
10063  }
10064  Utilities->CallLogPop(1099);
10065  return true;
10066  }
10067 
10068  if(Second == "Fns-sh") // third & fourth have to be headcodes
10069  {
10070  FormatType = FSHNewService;
10071  LocationType = AtLocation;
10072  SequenceType = Finish;
10073  ShuttleLinkType = ShuttleLink;
10074  if(!CheckHeadCodeValidity(5, GiveMessages, Third))
10075  {
10076  Utilities->CallLogPop(1050);
10077  return false;
10078  }
10079  if(!CheckHeadCodeValidity(6, GiveMessages, Fourth))
10080  {
10081  Utilities->CallLogPop(1051);
10082  return false;
10083  }
10084  Utilities->CallLogPop(1052);
10085  return true;
10086  }
10087 
10088  // new segment for 'pas'
10089  if(Second == "pas") // third has to be a location
10090  {
10091  FormatType = PassTime;
10092  LocationType = EnRoute;
10093  SequenceType = Intermediate;
10094  ShuttleLinkType = NotAShuttleLink;
10095  if(!CheckLocationValidity(2, Third, GiveMessages, CheckLocationsExistInRailway))
10096  {
10097  Utilities->CallLogPop(1515);
10098  return false;
10099  }
10100  Utilities->CallLogPop(1516);
10101  return true;
10102  }
10103 
10104  // new segment for revised 'Fer'
10105  if(Second == "Fer")
10106  // third has to be a set of IDs separated by spaces, and at least 1
10107  {
10108  FormatType = ExitRailway;
10109  LocationType = EnRoute;
10110  SequenceType = Finish;
10111  ShuttleLinkType = NotAShuttleLink;
10112  if(CheckLocationsExistInRailway)
10113  {
10114  if(!CheckAndPopulateListOfIDs(0, Third, ExitList, GiveMessages))
10115  {
10116  Utilities->CallLogPop(1519);
10117  return false;
10118  }
10119  }
10120  Utilities->CallLogPop(1520);
10121  return true;
10122  }
10123 
10124  // all remainder must be TimeCmdHeadCode types to be valid
10125  if((Second != "Fns") && (Second != "Fjo") && (Second != "jbo") && (Second != "fsp") && (Second != "rsp") && (Second != "Sfs") && (Second != "Sns") &&
10126  (Second != "Frh-sh"))
10127  {
10128  Utilities->CallLogPop(922);
10129  return false; // all TimeCmdHeadCode types
10130  }
10131 
10132  if(!CheckHeadCodeValidity(7, GiveMessages, Third))
10133  {
10134  Utilities->CallLogPop(923);
10135  return false;
10136  }
10137  FormatType = TimeCmdHeadCode;
10138  LocationType = AtLocation;
10139  if(Second == "Frh-sh")
10140  ShuttleLinkType = ShuttleLink;
10141  else
10142  ShuttleLinkType = NotAShuttleLink;
10143  if((Second == "Fns") || (Second == "Fjo") || (Second == "Frh-sh"))
10144  {
10145  SequenceType = Finish;
10146  }
10147  if((Second == "jbo") || (Second == "fsp") || (Second == "rsp"))
10148  {
10149  SequenceType = Intermediate;
10150  }
10151  if((Second == "Sfs") || (Second == "Sns"))
10152  {
10153  SequenceType = Start;
10154  }
10155  Utilities->CallLogPop(924);
10156  return true;
10157 }
10158 
10159 // ---------------------------------------------------------------------------
10160 
10161 bool TTrainController::CheckLocationValidity(int Caller, AnsiString LocStr, bool GiveMessages, bool CheckLocationsExistInRailway)
10162 {
10163  // check that the location name exists in the railway (only if CheckLocationsExistInRailway is true), doesn't begin with a number
10164  // and contains no special characters
10165  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckLocationValidity," + LocStr);
10166  if(LocStr == "")
10167  {
10168  Utilities->CallLogPop(1353);
10169  return false; // has to have at least one character
10170  }
10171  if((LocStr[1] >= '0') && (LocStr[1] <= '9'))
10172  {
10173  Utilities->CallLogPop(1354);
10174  return false; // can't begin with a number
10175  }
10176  for(int x = 1; x < LocStr.Length() + 1; x++)
10177  {
10178  if(LocStr[x] < ' ')
10179  {
10180  Utilities->CallLogPop(1355);
10181  return false; // contains a special character
10182  }
10183  if(LocStr[x] > 'z')
10184  {
10185  Utilities->CallLogPop(1356);
10186  return false; // contains a character outside the standard ASCII set
10187  }
10188  }
10189  // check exists in railway location list if CheckLocationsExistInRailway is true
10190  if(CheckLocationsExistInRailway)
10191  {
10192  if(!Track->TimetabledLocationNameAllocated(3, LocStr))
10193  {
10194  TimetableMessage(GiveMessages, "Location name '" + LocStr +
10195  "' appears in the timetable but is not a valid name. To be valid the name must be a stopping location and apply to one or more platforms " +
10196  "(not concourses on their own), or to track at a blue non-station named location. BUT NOTE THAT trains can't stop at continuations so a name " +
10197  "that includes a continuation will not be valid.");
10198  Utilities->CallLogPop(1357);
10199  return false;
10200  }
10201  }
10202  Utilities->CallLogPop(1358);
10203  return true;
10204 }
10205 
10206 // ---------------------------------------------------------------------------
10207 
10208 bool TTrainController::CheckHeadCodeValidity(int Caller, bool GiveMessages, AnsiString HeadCode)
10209 {
10210  // if(!AnyHeadCodeValid) up to 8 characters total & last 4 characters must be NLNN where N = number and L = capital or small letter
10211  // if(AnyHeadCodeValid) up to 8 characters total, last 2 chars must be digits & last but 2 can be any alphanumeric, upper or lower case
10212  // NOTE: As of v0.6b AnyHeadCodeValid dropped, all headcodes are unrestricted
10213  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString((short)GiveMessages) + ",CheckHeadCodeValidity," +
10214  HeadCode);
10215  if((HeadCode.Length() < 4) || (HeadCode.Length() > 8))
10216  {
10217  TimetableMessage(GiveMessages, "Headcode error in '" + HeadCode +
10218  "', length must be between 4 and 8 characters, and last 4 must be a legitimate headcode. This error can also be caused by omitting a service reference after Snt-sh, Sns-sh, Fns-sh or Frh-sh");
10219  Utilities->CallLogPop(1359);
10220  return false;
10221  }
10222  // firstly allow any printable character (ASCII >= CHAR(32) & <= CHAR(126)), as these allowed in 1st 4 characters
10223  for(int x = 1; x < (HeadCode.Length() + 1); x++)
10224  {
10225  if((HeadCode[x] < ' ') || (HeadCode[x] > '~'))
10226  {
10227  TimetableMessage(GiveMessages, "Non-printable character in headcode '" + HeadCode + "'");
10228  Utilities->CallLogPop(1895);
10229  return false;
10230  }
10231  }
10232  // secondly ensure the true Headcode only has letters or digits
10233  for(int x = 3; x >= 0; x--)
10234  {
10235  if(((HeadCode[HeadCode.Length() - x] < 'A') || (HeadCode[HeadCode.Length() - x] > 'Z')) && ((HeadCode[HeadCode.Length() - x] < 'a') ||
10236  (HeadCode[HeadCode.Length() - x] > 'z')) && ((HeadCode[HeadCode.Length() - x] < '0') || (HeadCode[HeadCode.Length() - x] > '9')))
10237  {
10238  TimetableMessage(GiveMessages, "Headcode error in '" + HeadCode + "', headcode must consist of letters and digits only");
10239  Utilities->CallLogPop(1790);
10240  return false;
10241  }
10242  }
10243  Utilities->CallLogPop(1364);
10244  return true;
10245 }
10246 
10247 // ---------------------------------------------------------------------------
10248 
10249 bool TTrainController::CheckAndPopulateListOfIDs(int Caller, AnsiString IDSet, TExitList &ExitList, bool GiveMessages)
10250  // set of legitimate track element IDs, separated by spaces, and at least 1 present
10251 {
10252  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSetOfIDs," + IDSet);
10253  ExitList.clear();
10254  AnsiString CurrentID = "";
10255 
10256  if(IDSet.Length() == 0)
10257  {
10258  TimetableMessage(GiveMessages, "Must have at least one exit element ID following 'Fer'");
10259  Utilities->CallLogPop(1521);
10260  return false;
10261  }
10262  for(int x = 1; x <= IDSet.Length(); x++)
10263  {
10264  char C = IDSet[x];
10265  if(((C < '0') || (C > '9')) && (C != ' ') && (C != 'N') && (C != '-'))
10266  {
10267  TimetableMessage(GiveMessages, "Illegal character in the set of element IDs following 'Fer' in '" + IDSet + "'");
10268  Utilities->CallLogPop(1522);
10269  return false;
10270  }
10271  }
10272  int Pos = IDSet.Pos(' '); // look for the first space
10273 
10274  while(true)
10275  {
10276  if(Pos == 0)
10277  {
10278  CurrentID = IDSet;
10279  IDSet = "";
10280  }
10281  else
10282  {
10283  CurrentID = IDSet.SubString(1, Pos - 1);
10284  IDSet = IDSet.SubString(Pos + 1, IDSet.Length() - Pos);
10285  }
10286  int VecPos = Track->GetTrackVectorPositionFromString(7, CurrentID, GiveMessages);
10287  if(VecPos == -1)
10288  {
10289  Utilities->CallLogPop(1523);
10290  return false; // messages given in GetTrackVectorPositionFromString
10291  }
10292  else
10293  {
10294  if(Track->TrackElementAt(722, VecPos).TrackType != Continuation)
10295  {
10296  TimetableMessage(GiveMessages, "The element ID '" + CurrentID + "' following 'Fer' is not an exit");
10297  Utilities->CallLogPop(1524);
10298  return false;
10299  }
10300  else
10301  {
10302  // first check for duplicates
10303  if(!ExitList.empty())
10304  {
10305  for(TExitListIterator ELIT = ExitList.begin(); ELIT != ExitList.end(); ELIT++)
10306  {
10307  if(*ELIT == VecPos)
10308  {
10309  TimetableMessage(GiveMessages, "The element ID '" + CurrentID + "' following 'Fer' duplicates an earlier element");
10310  Utilities->CallLogPop(1532);
10311  return false;
10312  }
10313  }
10314  }
10315  // of OK add it to the list
10316  ExitList.push_back(VecPos);
10317  }
10318  }
10319  if(IDSet == "")
10320  {
10321  Utilities->CallLogPop(1525);
10322  return true;
10323  }
10324  else
10325  {
10326  Pos = IDSet.Pos(' '); // look for the next space
10327  }
10328  } // while(true)
10329 }
10330 
10331 // ---------------------------------------------------------------------------
10332 bool TTrainController::SplitTrainInfo(int Caller, AnsiString TrainInfoStr, AnsiString &HeadCode, AnsiString &Description, int &StartSpeed, int &MaxRunningSpeed,
10333  int &Mass, double &MaxBrakeRate, double &PowerAtRail, int &SignallerSpeed, bool GiveMessages)
10334  // 7 or 8 items for a new train (6 or 7 semicolons), for a continuing service only need headcode, though can have a description, if other
10335  // data entered for continuing service then will be ignored - message given to warn user, checks appropriate number of items and validity
10336  // of each item
10337 {
10338  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SplitTrainInfo," + TrainInfoStr);
10339  int Pos = 0;
10340  AnsiString Remainder = "";
10341  int SemiColonCount = 0;
10342 
10343  for(int x = 1; x < TrainInfoStr.Length() + 1; x++)
10344  {
10345  if(TrainInfoStr[x] == ';')
10346  SemiColonCount++;
10347  }
10348  if((SemiColonCount != 6) && (SemiColonCount != 7) && (SemiColonCount != 1) && (SemiColonCount != 0))
10349  {
10350  TimetableMessage(GiveMessages, "Error in train information in '" + TrainInfoStr +
10351  "'. Should be headcode + optional description for a continuing service;" +
10352  " or headcode, description, start speed, max running speed, mass, brake force, power (and optional signaller max. speed) for a new service");
10353  Utilities->CallLogPop(880);
10354  return false;
10355  }
10356  if(SemiColonCount == 0)
10357  {
10358  HeadCode = TrainInfoStr;
10359  if(!CheckHeadCodeValidity(8, GiveMessages, HeadCode))
10360  {
10361  Utilities->CallLogPop(881);
10362  return false;
10363  }
10364  Utilities->CallLogPop(882);
10365  return true;
10366  }
10367  if(SemiColonCount == 1) // headcode & description only
10368  {
10369  Pos = TrainInfoStr.Pos(';'); // 1st delimiter
10370  HeadCode = TrainInfoStr.SubString(1, Pos - 1);
10371  Description = TrainInfoStr.SubString(Pos + 1, TrainInfoStr.Length() - Pos);
10372  if(!CheckHeadCodeValidity(9, GiveMessages, HeadCode))
10373  {
10374  Utilities->CallLogPop(883);
10375  return false;
10376  }
10377  if(Description == "")
10378  {
10379  TimetableMessage(GiveMessages, "Train description missing in '" + TrainInfoStr + "'");
10380  Utilities->CallLogPop(884);
10381  return false;
10382  }
10383  if(Description.Length() > 60)
10384  {
10385  TimetableMessage(GiveMessages, "Train description too long, limit of 60 characters '" + TrainInfoStr + "'");
10386  Utilities->CallLogPop(1157);
10387  return false;
10388  }
10389  for(int x = 1; x < Description.Length() + 1; x++)
10390  {
10391  if((Description[x] < ' ') || (Description[x] > '~'))
10392  {
10393  TimetableMessage(GiveMessages, "Train description contains invalid characters in '" + TrainInfoStr + "'");
10394  Utilities->CallLogPop(885);
10395  return false;
10396  }
10397  }
10398  Utilities->CallLogPop(886);
10399  return true;
10400  }
10401  // if here must have 6 or 7 semicolons
10402  Pos = TrainInfoStr.Pos(';'); // 1st delimiter
10403  HeadCode = TrainInfoStr.SubString(1, Pos - 1);
10404  Remainder = TrainInfoStr.SubString(Pos + 1, TrainInfoStr.Length() - Pos);
10405  if(!CheckHeadCodeValidity(10, GiveMessages, HeadCode))
10406  {
10407  Utilities->CallLogPop(887);
10408  return false;
10409  }
10410  Pos = Remainder.Pos(';'); // 2nd delimiter
10411  Description = Remainder.SubString(1, Pos - 1);
10412  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
10413  if(Description == "")
10414  {
10415  TimetableMessage(GiveMessages, "Train description missing in '" + TrainInfoStr + "'");
10416  Utilities->CallLogPop(888);
10417  return false;
10418  }
10419  if(Description.Length() > 60)
10420  {
10421  TimetableMessage(GiveMessages, "Train description too long, limit of 60 characters '" + TrainInfoStr + "'");
10422  Utilities->CallLogPop(1158);
10423  return false;
10424  }
10425  for(int x = 1; x < Description.Length() + 1; x++)
10426  {
10427  if((Description[x] < ' ') || (Description[x] > 126))
10428  {
10429  TimetableMessage(GiveMessages, "Train description contains invalid characters in '" + TrainInfoStr + "'");
10430  Utilities->CallLogPop(889);
10431  return false;
10432  }
10433  }
10434  Pos = Remainder.Pos(';'); // 3rd delimiter
10435  AnsiString StartSpeedStr = Remainder.SubString(1, Pos - 1);
10436 
10437  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
10438  if(StartSpeedStr == "")
10439  {
10440  TimetableMessage(GiveMessages, "Train starting speed missing in '" + TrainInfoStr + "'");
10441  Utilities->CallLogPop(890);
10442  return false;
10443  }
10444  for(int x = 1; x < StartSpeedStr.Length() + 1; x++)
10445  {
10446  if((StartSpeedStr[x] < '0') || (StartSpeedStr[x] > '9'))
10447  {
10448  TimetableMessage(GiveMessages, "Train start speed contains invalid characters in '" + TrainInfoStr + "'");
10449  Utilities->CallLogPop(891);
10450  return false;
10451  }
10452  }
10453  StartSpeed = StartSpeedStr.ToInt();
10454  if(StartSpeed > TTrain::MaximumSpeedLimit) // 400kph = 250mph
10455  {
10456  StartSpeed = TTrain::MaximumSpeedLimit;
10457  if(!SSHigh) // added at v2.4.0
10458  {
10459  TimetableMessage(GiveMessages, "Train starting speed > 400km/h in '" + TrainInfoStr + "'. Setting it to 400km/h");
10460  SSHigh = true;
10461  }
10462  }
10463  Pos = Remainder.Pos(';'); // 4th delimiter
10464  AnsiString MaxRunningSpeedStr = Remainder.SubString(1, Pos - 1);
10465 
10466  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
10467  if(MaxRunningSpeedStr == "")
10468  {
10469  TimetableMessage(GiveMessages, "Train maximum running speed missing in '" + TrainInfoStr + "'");
10470  Utilities->CallLogPop(892);
10471  return false;
10472  }
10473  for(int x = 1; x < MaxRunningSpeedStr.Length() + 1; x++)
10474  {
10475  if((MaxRunningSpeedStr[x] < '0') || (MaxRunningSpeedStr[x] > '9'))
10476  {
10477  TimetableMessage(GiveMessages, "Train maximum running speed contains invalid characters in '" + TrainInfoStr + "'");
10478  Utilities->CallLogPop(893);
10479  return false;
10480  }
10481  }
10482  MaxRunningSpeed = MaxRunningSpeedStr.ToInt();
10483  if(MaxRunningSpeed > TTrain::MaximumSpeedLimit) // 400kph = 250mph
10484  {
10485  MaxRunningSpeed = TTrain::MaximumSpeedLimit;
10486  if(!MRSHigh) // added at v2.4.0
10487  {
10488  TimetableMessage(GiveMessages, "Train maximum running speed > 400km/h in '" + TrainInfoStr + "'. Setting it to 400km/h");
10489  MRSHigh = true;
10490  }
10491  }
10492  if(MaxRunningSpeed < 10)
10493  // changed at v0.6 to prevent low max speeds - can cause problems in SetTrainMovementValues
10494  {
10495  MaxRunningSpeed = 10;
10496  if(!MRSLow) // added at v2.4.0
10497  {
10498  TimetableMessage(GiveMessages, "Train maximum running speed can't be less than 10km/h in '" + TrainInfoStr + "', it will be set to 10km/h");
10499  MRSLow = true;
10500  }
10501  }
10502  Pos = Remainder.Pos(';'); // 5th delimiter
10503  AnsiString MassStr = Remainder.SubString(1, Pos - 1);
10504 
10505  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
10506  if(MassStr == "")
10507  {
10508  TimetableMessage(GiveMessages, "Train mass missing in '" + TrainInfoStr + "'");
10509  Utilities->CallLogPop(895);
10510  return false;
10511  }
10512  for(int x = 1; x < MassStr.Length() + 1; x++)
10513  {
10514  if((MassStr[x] < '0') || (MassStr[x] > '9'))
10515  {
10516  TimetableMessage(GiveMessages, "Train mass contains invalid characters in '" + TrainInfoStr + "'");
10517  Utilities->CallLogPop(896);
10518  return false;
10519  }
10520  }
10521  Mass = MassStr.ToInt() * 1000; // convert tonnes to kg
10522  if(Mass > TTrain::MaximumMassLimit) // 10,000tonnes
10523  {
10524  Mass = TTrain::MaximumMassLimit;
10525  if(!MassHigh) // added at v2.4.0
10526  {
10527  TimetableMessage(GiveMessages, "Train mass > 10,000 tonnes in '" + TrainInfoStr + "'. Setting it to 10,000 tonnes");
10528  MassHigh = true;
10529  }
10530  }
10531  if(Mass == 0)
10532  {
10533  TimetableMessage(GiveMessages, "Train mass zero in '" + TrainInfoStr + "'");
10534  Utilities->CallLogPop(897);
10535  return false;
10536  }
10537  Pos = Remainder.Pos(';'); // 6th delimiter
10538  AnsiString MaxBrakeForceStr = Remainder.SubString(1, Pos - 1);
10539 
10540  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
10541  if(MaxBrakeForceStr == "")
10542  {
10543  TimetableMessage(GiveMessages, "Train braking force missing in '" + TrainInfoStr + "'");
10544  Utilities->CallLogPop(898);
10545  return false;
10546  }
10547  for(int x = 1; x < (MaxBrakeForceStr.Length() + 1); x++)
10548  {
10549  if((MaxBrakeForceStr[x] != '.') && ((MaxBrakeForceStr[x] < '0') || (MaxBrakeForceStr[x] > '9')))
10550  {
10551  TimetableMessage(GiveMessages, "Train braking force contains invalid characters in '" + TrainInfoStr + "'");
10552  Utilities->CallLogPop(899);
10553  return false;
10554  }
10555  }
10556  double MaxBrakeForce = MaxBrakeForceStr.ToDouble() * 1000;
10557 
10558  // convert to kg force
10559  if((MaxBrakeForce / Mass) > 1) // gives 'g' braking - 9.81m/s/s
10560  {
10561  MaxBrakeForce = Mass;
10562  if(!BFHigh) // added at v2.4.0
10563  {
10564  TimetableMessage(GiveMessages, "Train braking force too high in '" + TrainInfoStr + "'. Setting it to the same as the train mass");
10565  BFHigh = true;
10566  }
10567  }
10568  if((MaxBrakeForce / Mass) < 0.01)
10569  {
10570  MaxBrakeForce = Mass * 0.01;
10571  if(!BFLow) // added at v2.4.0
10572  {
10573  TimetableMessage(GiveMessages, "Train braking force too low in '" + TrainInfoStr + "'. Setting it to 1% of the train mass");
10574  BFLow = true;
10575  }
10576  }
10577  // convert to m/s/s
10578  MaxBrakeRate = MaxBrakeForce / Mass * 9.81;
10579  // now may have just a power entry or power and signaller max. speed
10580  AnsiString GrossPowerStr = "", SignallerSpeedStr = "";
10581 
10582  if(SemiColonCount == 6)
10583  {
10584  GrossPowerStr = Remainder;
10585  SignallerSpeedStr = "30"; // default value
10586  }
10587  else // must be 7
10588  {
10589  Pos = Remainder.Pos(';'); // 7th delimiter
10590  GrossPowerStr = Remainder.SubString(1, Pos - 1);
10591  SignallerSpeedStr = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
10592  }
10593  // deal with GrossPower
10594  if(GrossPowerStr == "")
10595  {
10596  TimetableMessage(GiveMessages, "Train power missing in '" + TrainInfoStr + "'");
10597  Utilities->CallLogPop(901);
10598  return false;
10599  }
10600  for(int x = 1; x < GrossPowerStr.Length() + 1; x++)
10601  {
10602  if((GrossPowerStr[x] < '0') || (GrossPowerStr[x] > '9'))
10603  {
10604  TimetableMessage(GiveMessages, "Train power contains invalid characters in '" + TrainInfoStr + "'");
10605  Utilities->CallLogPop(902);
10606  return false;
10607  }
10608  }
10609 
10610  double GrossPower = GrossPowerStr.ToInt() * 1000; // convert to W
10611 
10612  if(GrossPower > TTrain::MaximumPowerLimit) // 100MW
10613  {
10614  GrossPower = TTrain::MaximumPowerLimit;
10615  if(!PwrHigh)
10616  {
10617  TimetableMessage(GiveMessages, "Train power > 100,000kW in '" + TrainInfoStr + "'. Setting it to 100,000kW");
10618  PwrHigh = true;
10619  }
10620  }
10621  else if(GrossPower == 0) // changed at v2.4.0
10622  {
10623  GrossPower = 0.1;
10624  // can't be zero or AValue is zero and then have divide by zero error, so set to 0.1W so acceleration tiny (though should be intercepted before accel calculated)
10625  }
10626  else if((GrossPower > 0) && (GrossPower < 10000))
10627  // added at v2.4.0 to ensure min power of 8kW at rail unless zero (otherwise could have too low AValues
10628  {
10629  GrossPower = 10000;
10630  }
10631  PowerAtRail = GrossPower * 0.8;
10632  // apply ratio of 80% for rail to gross power (seems about average from an internet search)
10633 
10634  // deal with SignallerSpeed
10635  if(SignallerSpeedStr == "")
10636  {
10637  TimetableMessage(GiveMessages, "Signaller speed not set in '" + TrainInfoStr + "', either set a value or remove the extra semicolon");
10638  Utilities->CallLogPop(1771);
10639  return false;
10640  }
10641  for(int x = 1; x < SignallerSpeedStr.Length() + 1; x++)
10642  {
10643  if((SignallerSpeedStr[x] < '0') || (SignallerSpeedStr[x] > '9'))
10644  {
10645  TimetableMessage(GiveMessages, "Signaller speed contains invalid characters in '" + TrainInfoStr + "'");
10646  Utilities->CallLogPop(1769);
10647  return false;
10648  }
10649  }
10650  SignallerSpeed = SignallerSpeedStr.ToInt();
10651  if(SignallerSpeed > TTrain::MaximumSpeedLimit)
10652  {
10653  SignallerSpeed = TTrain::MaximumSpeedLimit;
10654  if(!SigSHigh)
10655  {
10656  TimetableMessage(GiveMessages, "Signaller speed > 400km/h in '" + TrainInfoStr + "'. Setting it to 400km/h");
10657  SigSHigh = true;
10658  }
10659  }
10660  if(SignallerSpeed < 10)
10661  // changed at v0.6 to prevent low max speeds - can cause problems in SetTrainMovementValues
10662  {
10663  SignallerSpeed = 10;
10664  if(!SigSLow)
10665  {
10666  TimetableMessage(GiveMessages, "Signaller speed can't be less than 10km/h in '" + TrainInfoStr + "', it will be set to 10km/h");
10667  SigSLow = true;
10668  }
10669  // Utilities->CallLogPop(1770);
10670  // return false;
10671  }
10672  Utilities->CallLogPop(904);
10673  return true;
10674 }
10675 
10676 // ---------------------------------------------------------------------------
10677 
10678 bool TTrainController::SplitRepeat(int Caller, AnsiString OneEntry, int &RearStartOrRepeatMins, int &FrontStartOrRepeatDigits, int &NumberOfRepeats,
10679  bool GiveMessages)
10680 {
10681  // Format must be: R;mm;dd;nn mm may be 1, 2 or more digits, dd may be 1 or 2 digits, nn may be 1, 2 or more digits
10682  // function checks validity of each item and returns false for error
10683  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SplitRepeat," + OneEntry);
10684  if(OneEntry.Length() < 7)
10685  {
10686  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - should be 'R;m;d;n'");
10687  Utilities->CallLogPop(865);
10688  return false;
10689  }
10690  int SemiColonCount = 0;
10691 
10692  for(int x = 1; x < OneEntry.Length() + 1; x++)
10693  {
10694  if(OneEntry[x] == ';')
10695  SemiColonCount++;
10696  }
10697  if(SemiColonCount != 3)
10698  {
10699  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - should be 'R;m;d;n'");
10700  Utilities->CallLogPop(866);
10701  return false;
10702  }
10703  if((OneEntry[1] != 'R') || (OneEntry[2] != ';'))
10704  {
10705  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - should be 'R;m;d;n'");
10706  Utilities->CallLogPop(867);
10707  return false;
10708  }
10709  AnsiString Remainder = OneEntry.SubString(3, OneEntry.Length() - 2);
10710  // strip off R;
10711 
10712  int Pos = 0;
10713 
10714  Pos = Remainder.Pos(';');
10715  AnsiString MinutesStr = Remainder.SubString(1, Pos - 1);
10716 
10717  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
10718  if(MinutesStr == "")
10719  {
10720  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - minute increment segment missing");
10721  Utilities->CallLogPop(868);
10722  return false;
10723  }
10724  if(MinutesStr.Length() > 3)
10725  // added for v2.3.1 following Albie Vowles' reported error in repeat value 03/02/20
10726  {
10727  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - minute value too high, maximum value is 999");
10728  Utilities->CallLogPop(2119);
10729  return false;
10730  }
10731  for(int x = 1; x < MinutesStr.Length() + 1; x++)
10732  {
10733  if((MinutesStr[x] < '0') || (MinutesStr[x] > '9'))
10734  {
10735  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - non-digit character in minute increment segment");
10736  Utilities->CallLogPop(869);
10737  return false;
10738  }
10739  }
10740  RearStartOrRepeatMins = MinutesStr.ToInt();
10741  if(RearStartOrRepeatMins == 0)
10742  {
10743  TimetableMessage(GiveMessages, "Repeat minute increment is zero in: '" + OneEntry + "' - can't have a zero value");
10744  Utilities->CallLogPop(870);
10745  return false;
10746  }
10747  Pos = Remainder.Pos(';');
10748  AnsiString DigitsStr = Remainder.SubString(1, Pos - 1);
10749 
10750  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
10751  if(DigitsStr == "")
10752  {
10753  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - headcode increment segment missing");
10754  Utilities->CallLogPop(871);
10755  return false;
10756  }
10757  for(int x = 1; x < DigitsStr.Length() + 1; x++)
10758  {
10759  if((DigitsStr[x] < '0') || (DigitsStr[x] > '9'))
10760  {
10761  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - non-digit character in headcode increment segment");
10762  Utilities->CallLogPop(872);
10763  return false;
10764  }
10765  }
10766  if(DigitsStr.Length() > 2)
10767  {
10768  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - maximum number of digits for headcode increment is 2");
10769  Utilities->CallLogPop(873);
10770  return false;
10771  }
10772  FrontStartOrRepeatDigits = DigitsStr.ToInt();
10773 /* allow zero digit increments so HC can stay same for repeated services - for many suburban services the headcode digits relate to the
10774  route rather than the service
10775  if(FrontStartOrRepeatDigits == 0)
10776  {
10777  TimetableMessage(GiveMessages, "Repeat headcode increment is zero in: '" + OneEntry + "' - can't have a zero value");
10778  Utilities->CallLogPop(874);
10779  return false;
10780  }
10781 */
10782  if(!Last2CharactersBothDigits(0, ServiceReference) && (FrontStartOrRepeatDigits > 0))
10783  // new for v0.6b for unrestricted headcodes
10784  {
10785  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry +
10786  "' - a repeating service with incrementing digits must have digits as its last two headcode characters");
10787  Utilities->CallLogPop(1889);
10788  return false;
10789  }
10790  AnsiString NumberStr = Remainder;
10791 
10792  if(NumberStr == "")
10793  {
10794  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - number of repeats missing");
10795  Utilities->CallLogPop(875);
10796  return false;
10797  }
10798  if(NumberStr.Length() > 4)
10799  // added for v2.3.1 following Albie Vowles' reported error 03/02/20
10800  {
10801  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - repeat value too high, no timetabled event can exceed 95 hours & 59 minutes");
10802  Utilities->CallLogPop(2118);
10803  return false;
10804  }
10805  for(int x = 1; x < NumberStr.Length() + 1; x++)
10806  {
10807  if((NumberStr[x] < '0') || (NumberStr[x] > '9'))
10808  // catches negative numbers
10809  {
10810  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - non-digit character in number of repeats");
10811  Utilities->CallLogPop(876);
10812  return false;
10813  }
10814  }
10815  NumberOfRepeats = NumberStr.ToInt();
10816  if(NumberOfRepeats == 0)
10817  {
10818  TimetableMessage(GiveMessages, "Number of repeats is zero in: '" + OneEntry + "' - if no repeats are needed the repeat should be omitted");
10819  Utilities->CallLogPop(877);
10820  return false;
10821  }
10822  Utilities->CallLogPop(878);
10823  return true;
10824 }
10825 
10826 // ---------------------------------------------------------------------------
10827 
10828 bool TTrainController::SecondPassActions(int Caller, bool GiveMessages)
10829 /* Note that here the TrainDataVector has been compiled with FinalCall true in ProcessOneTimetableLine so work on the
10830  vector rather than the timetable
10831  Note also that for unlocated Snt entries the LocationType hasn't yet been set
10832 
10833  Many of the errors caught here duplicate those in the preliminary checks, but leave in for completeness
10834 
10835  For info:-
10836  class TActionVectorEntry //contains a single train action - repeat entry is also of this class though no train action is taken for it
10837  {
10838  public:
10839  AnsiString LocationName, Command, OtherHeadCode, NonRepeatingShuttleLinkHeadCode; ///< string values for timetabled action entries, null
10841  bool SignallerControl; ///< indicates a train that is defined by the timetable as under signaller control
10842  bool Warning; ///< if set triggers an alert in the warning panel when the action is reached
10843  int NumberOfRepeats; ///< the number of repeating services
10844  int RearStartOrRepeatMins, FrontStartOrRepeatDigits; ///< dual-purpose variables used for the TrackVectorPositions of the rear and front
10846  TDateTime EventTime, ArrivalTime, DepartureTime; ///< relevant times at which the action is timetabled, zeroed on creation so change
10848  TExitList ExitList; ///< the list of valid train exit TrackVector positions for 'Fer' entries (empty to begin with)
10849  TTimetableFormatType FormatType; ///< defines the timetable action type
10850  TTimetableLocationType LocationType; ///< indicates where the train is when the relevant action occurs
10851  TTimetableSequenceType SequenceType; ///< indicates where in the sequence of codes the action lies
10852  TTimetableShuttleLinkType ShuttleLinkType; ///< indicates whether or not the action relates to a shuttle service link
10853  TTrainDataEntry *LinkedTrainEntryPtr; ///< link pointer for use between fsp/rsp & Sfs; Fjo & jbo; Fns & Sns; & all shuttle to shuttle
10855  TTrainDataEntry *NonRepeatingShuttleLinkEntryPtr; ///< pointer used by shuttles for the non-shuttle train links, in & out, the
10857 
10858  // inline function
10859 
10861  TActionVectorEntry() {
10862  RearStartOrRepeatMins=0; FrontStartOrRepeatDigits=0; NumberOfRepeats=0; FormatType=NoFormat;
10863  SequenceType=NoSequence; LocationType=NoLocation; ShuttleLinkType=NoShuttleLink, EventTime=TDateTime(-1);
10864  ArrivalTime=TDateTime(-1); DepartureTime=TDateTime(-1); LinkedTrainEntryPtr=0; NonRepeatingShuttleLinkEntryPtr=0;
10865  Warning = false; SignallerControl = false;
10866  }
10867  };
10868 
10869  typedef std::vector<TActionVectorEntry> TActionVector;//contains all actions for a single train
10870 
10871  class TTrainDataEntry //contains all data for a single train - copied into train object when becomes active
10872  {
10873  public:
10874  AnsiString HeadCode, ServiceReference, Description; ///< headcode is the first train's headcode, rest are calculated from repeat
10877  double MaxBrakeRate; ///< in metres/sec/sec
10878  double MaxRunningSpeed; ///< in km/h
10879  double PowerAtRail; ///< in Watts (taken as 80% of the train's Gross Power, i.e. that entered by the user)
10880  int Mass; ///< in kg
10881  int NumberOfTrains; ///< number of repeats + 1
10882  int SignallerSpeed; ///< in km/h for use when under signaller control
10883  int StartSpeed; ///< in km/h
10884  TActionVector ActionVector; ///< all the actions for the train
10885  TTrainOperatingDataVector TrainOperatingDataVector; ///< operating information for the train including all its repeats
10886 
10887  //inline function
10888 
10890  TTrainDataEntry()
10891  {
10892  StartSpeed=0; MaxRunningSpeed=0; NumberOfTrains=0;
10893  }
10894  };
10895 
10896  Allowable successors:-
10897  Snt unlocated -> Fer, TimeLoc (arr), TimeTimeLoc, (new) pas; No others
10898  Snt located -> No starts, no finishes except Frh & Fjo (as of v2.0.0), no repeat, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK
10899  Snt-sh -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK
10900  Sfs -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
10901  set location, else fails)
10902  Sns -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
10903  set location, else fails)
10904  Sns-sh -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
10905  set location, else fails)
10906  Sns-fsh -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
10907  set location, else fails)
10908  Fns -> R only
10909  F-nshs -> Nothing (no repeats permitted)
10910  Fjo -> R only
10911  Frh -> R only
10912  Fer -> R only
10913  Frh-sh -> R only
10914  Fns-sh -> R only
10915  jbo -> No starts, finishes, repeats, splits, pas or TimeTimeLoc; TimeLoc (dep), jbo or cdt OK
10916  fsp -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK
10917  rsp -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK
10918  cdt -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK
10919  TimeLoc (arr) -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK
10920  TimeLoc (dep) -> Fer, TimeLoc (arr), or TimeTimeLoc, (new) pas OK, no others
10921  TimeTimeLoc -> Fer, TimeLoc (arr), or TimeTimeLoc, (new) pas OK, no others
10922  (new) pas -> Fer, TimeLoc (arr), or TimeTimeLoc, (new) pas OK, no others
10923  Repeat -> Nothing
10924 
10925  There must be a TimeLoc arrival (or a Sns start at location) in a sequence so successive cmd locations can be set
10926  Check all Snt's & set Locations if located (located = zero start speed, either element at a location (but if rear element
10927  is a continuation then treated as unlocated), and location listed in the next TimeLoc entry, though needn't be immediately after)
10928  If Snt entry at a location specified in a following TimeLoc entry but start speed > 0 give error message
10929  Check all times increase or stay same through ActionVector
10930  Cycle through all entries in vector setting arr & dep times based on above list
10931  Add locations to all relevant cmd entries based on earlier arrival location (or earlier reference for Sfs & Sns)
10932  Check locations match the arr & dep TimeLoc entries
10933  Check same location doesn't appear twice before a cdt except for separate arr & dep TimeLocs
10934  Make above valid succession checks
10935  Check all splits have matching Sfs headcodes (both ways), add locations to SFSs & check times same
10936  Check all new service headcodes (Sns) have matching headcodes (both ways), add locations to SNHs & check times same
10937  Check a split to 'x' doesn't again split to 'x' (anywhere, not just for one train, since headcodes can be duplicated)
10938  Check each Fns has matching Sns headcodes (both ways), add locations to SNHs & check times same
10939  Check all joins have matching headcodes (both ways), locations & times & don't occur in same sequence
10940  Check each joined by train not joined by same train again (anywhere, not just for one train, since headcodes can be duplicated)
10941  Set train info for Sfs & Sns entries
10942  Check each repeat entry exactly matches any included joins or splits (user has to enter it to show that really wants it)
10943  Check at least one platform long enough for a split (only need 2 lengths) & disallow if not, need length of 2 & 1 extra
10944  element at each end, or length of 3 & 1 extra element at either end
10945  Check all TimeLocs have either Arr or Dep times set and EventTime == -1
10946  Check all Cmds have EventTime set & Arr & Dep times = -1
10947  Check all Sfs & Sns entries followed somewhere in sequence by a TimeLoc departure
10948  Check all locations except unlocated Snts, Fers and Repeats have a LocationName
10949 
10950  Give messages in function if errors detected and clear the vector. Return false for failure.
10951 */
10952 
10953 /* Earlier checks:-
10954  Checks carried out with error messages in this function:-
10955  At least one comma in the line (it's based on a csv file);
10956  No entries following train information;
10957  At least one comma in remainder after train information (i.e at least a start and a finish entry);
10958  SplitEntry returns false in an intermediate entry - message repeats the entry for information;
10959  First entry not a start entry;
10960  Train information incomplete before a start entry;
10961  Entry follows a finish entry but doesn't begin with 'R';
10962  SplitEntry returns false in a finish entry - message repeats the entry for information;
10963  Last action entry isn't a finish entry.
10964 
10965  Function returns false with no message if:-
10966  Timetable start time invalid (no message is given for an invalid time as the line is assumed to be an irrelevant line; if no start
10967  time is found at all then an error message is given in the calling function);
10968  SplitTrainInfo returns false (message given in called function);
10969  SplitRepeat returns false (message given in called function).
10970 */ {
10971  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SecondPassActions,");
10972  if(TrainDataVector.empty())
10973  {
10974  SecondPassMessage(GiveMessages, "Error in timetable - there appear to be no train services in the timetable, it must contain at least one");
10975  TrainDataVector.clear();
10976  Utilities->CallLogPop(1832);
10977  return false;
10978  }
10979 
10980 /* new preliminary checks for v0.2b without changing anything, carry each out separately:-
10981  1) must have at least one actionvector entry
10982  2) if first actionvector entry not SignallerControl then must have at least one more actionvector entry
10983  3) if first actionvector entry is SignallerControl then must have no more actionvector entries except a repeat
10984  4) first entry must be a start;
10985  4a) if first entry is Snt and not signallercontrol and second is a finish then it can only be Frh, Fjo or Fer//added for v2.0.0
10986  5) a start must be the first entry;
10987  6) a repeat entry must be the last;
10988  7) for other than SignallerControl the last entry must be repeat or finish; if last entry is a repeat the last but one must be a finish;
10989  8) a finish entry must be the last or last but one, and if last but one the last must be a repeat
10990  Other successor errors will be caught later as all 'throws' changed to messages prior to the bulk of the sucessor checks
10991 */
10992 
10993  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (1)
10994  {
10995  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
10996  if(TrainDataVector.at(x).ActionVector.empty())
10997  {
10998  SecondPassMessage(GiveMessages, "Error in timetable - the following service has no listed events, there must be at least one: " + TDEntry.HeadCode);
10999  TrainDataVector.clear();
11000  Utilities->CallLogPop(1833);
11001  return false;
11002  }
11003  }
11004  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (2)
11005  {
11006  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11007  TActionVectorEntry AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
11008  if(!(AVEntry0.SignallerControl))
11009  {
11010  if(TrainDataVector.at(x).ActionVector.size() == 1)
11011  {
11012  SecondPassMessage(GiveMessages, "Error in timetable - service must have a start event and at least one other for: " + TDEntry.HeadCode);
11013  TrainDataVector.clear();
11014  Utilities->CallLogPop(1822);
11015  return false;
11016  }
11017  }
11018  }
11019  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (3)
11020  {
11021  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11022  TActionVectorEntry AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
11023  if(AVEntry0.SignallerControl)
11024  {
11025  if(TrainDataVector.at(x).ActionVector.size() > 2)
11026  {
11027  SecondPassMessage(GiveMessages,
11028  "Error in timetable - a signaller control service can have no more than one item (a repeat) after the start event, see: " +
11029  TDEntry.HeadCode);
11030  TrainDataVector.clear();
11031  Utilities->CallLogPop(1837);
11032  return false;
11033  }
11034  if(TrainDataVector.at(x).ActionVector.size() > 1)
11035  {
11036  TActionVectorEntry AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
11037  if(AVEntry1.FormatType != Repeat)
11038  {
11039  SecondPassMessage(GiveMessages,
11040  "Error in timetable - a signaller control service cannot have any other than a repeat after the start event, see: " + TDEntry.HeadCode);
11041  TrainDataVector.clear();
11042  Utilities->CallLogPop(1838);
11043  return false;
11044  }
11045  }
11046  }
11047  }
11048  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (4)
11049  {
11050  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11051  TActionVectorEntry AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
11052  if(AVEntry0.SequenceType != Start)
11053  {
11054  SecondPassMessage(GiveMessages, "Error in timetable - the first event must be a start for: " + TDEntry.HeadCode);
11055  TrainDataVector.clear();
11056  Utilities->CallLogPop(1824);
11057  return false;
11058  }
11059  if((AVEntry0.Command == "Snt") && !(AVEntry0.SignallerControl))
11060  // 4a added at v2.0.0. This is only a rough check, Fer only valid for an unlocated Snt
11061  // and others for a located Snt, but those checks done later
11062  {
11063  TActionVectorEntry AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
11064  // must be a second entry if first not signallercontrol
11065  if((AVEntry1.SequenceType == Finish) && ((AVEntry1.Command == "Fns-sh") || (AVEntry1.Command == "Frh-sh")))
11066  {
11067  SecondPassMessage(GiveMessages, "Error in timetable - finish events Fns-sh and Frh-sh not permitted immediately after an Snt entry for: " +
11068  TDEntry.HeadCode);
11069  TrainDataVector.clear();
11070  Utilities->CallLogPop(2046);
11071  return false;
11072  }
11073  }
11074  }
11075  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (5)
11076  {
11077  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11078  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11079  {
11080  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11081  if((AVEntry.SequenceType == Start) && (y != 0))
11082  {
11083  SecondPassMessage(GiveMessages, "Error in timetable - a start event is present that is not the first event for: " + TDEntry.HeadCode);
11084  TrainDataVector.clear();
11085  Utilities->CallLogPop(1825);
11086  return false;
11087  }
11088  }
11089  }
11090  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (6)
11091  {
11092  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11093  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11094  {
11095  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11096  if((AVEntry.FormatType == Repeat) && (y != (TrainDataVector.at(x).ActionVector.size() - 1)))
11097  {
11098  SecondPassMessage(GiveMessages, "Error in timetable - a repeat is present that is not the last item for: " + TDEntry.HeadCode);
11099  TrainDataVector.clear();
11100  Utilities->CallLogPop(1826);
11101  return false;
11102  }
11103  }
11104  }
11105  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (7)
11106  {
11107  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11108  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11109  {
11110  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11111  if((y == 0) && AVEntry.SignallerControl)
11112  {
11113  break;
11114  }
11115  if(y == (TrainDataVector.at(x).ActionVector.size() - 1))
11116  {
11117  if((AVEntry.FormatType != Repeat) && (AVEntry.SequenceType != Finish))
11118  {
11119  SecondPassMessage(GiveMessages, "Error in timetable - the last item must be either a finish event or a repeat for: " + TDEntry.HeadCode);
11120  TrainDataVector.clear();
11121  Utilities->CallLogPop(1827);
11122  return false;
11123  }
11124  if(AVEntry.FormatType == Repeat)
11125  {
11126  const TActionVectorEntry &LastAVEntry = TrainDataVector.at(x).ActionVector.at(y - 1);
11127  if(LastAVEntry.SequenceType != Finish)
11128  {
11129  SecondPassMessage(GiveMessages, "Error in timetable - the last event before the repeat must be a finish for: " + TDEntry.HeadCode);
11130  TrainDataVector.clear();
11131  Utilities->CallLogPop(1828);
11132  return false;
11133  }
11134  }
11135  }
11136  }
11137  }
11138  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (8)
11139  {
11140  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11141  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11142  {
11143  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11144  if(AVEntry.SequenceType == Finish)
11145  {
11146  if((y != (TrainDataVector.at(x).ActionVector.size() - 1)) && (y != (TrainDataVector.at(x).ActionVector.size() - 2)))
11147  {
11148  SecondPassMessage(GiveMessages, "Error in timetable - a finish event must be either the last or last but one for: " + TDEntry.HeadCode);
11149  TrainDataVector.clear();
11150  Utilities->CallLogPop(1829);
11151  return false;
11152  }
11153  if(y == (TrainDataVector.at(x).ActionVector.size() - 2))
11154  {
11155  if(TrainDataVector.at(x).ActionVector.at(y + 1).FormatType != Repeat)
11156  {
11157  SecondPassMessage(GiveMessages, "Error in timetable - the only event that can follow a finish event is a repeat for: " +
11158  TDEntry.HeadCode);
11159  TrainDataVector.clear();
11160  Utilities->CallLogPop(1830);
11161  return false;
11162  }
11163  }
11164  }
11165  }
11166  }
11167 
11168  // end of new preliminary checks
11169 
11170  // check ActionVector present and check start event successor validity
11171  // For Snt & Snt-sh set location if stopped, don't set any times yet
11172  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
11173  {
11174  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11175  TActionVectorEntry & AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
11176  // use reference so can change internals where necessary
11177  if((AVEntry0.Command == "Snt") || (AVEntry0.Command == "Snt-sh"))
11178  {
11179  AnsiString LocationName = "";
11180  if(IsSNTEntryLocated(0, TDEntry, LocationName))
11181  // it is at a location
11182  {
11183  if(TDEntry.StartSpeed == 0) // stopped
11184  {
11185  AVEntry0.LocationName = LocationName;
11186  AVEntry0.LocationType = AtLocation;
11187  // check successor validity for located Snt that isn't a SignallerControl entry
11188  if(!AVEntry0.SignallerControl)
11189  {
11190  const TActionVectorEntry &AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
11191  // at least 2 entries present checked in integrity check so (1) valid
11192  if(!AtLocSuccessor(AVEntry1))
11193  {
11194  // Frh following Snt-sh will return false in location check, so no need to check here
11195  SecondPassMessage(GiveMessages, "Error in timetable - stopped 'Snt' or 'Snt-sh' followed by an illegal event for: " +
11196  TDEntry.HeadCode + ". The event isn't valid for a stationary train.");
11197  TrainDataVector.clear();
11198  Utilities->CallLogPop(523);
11199  return false;
11200  }
11201  }
11202  }
11203  else
11204  {
11205  SecondPassMessage(GiveMessages, "Error in timetable - 'Snt' or 'Snt-sh' event at stop location but start speed not zero for: " +
11206  TDEntry.HeadCode);
11207  TrainDataVector.clear();
11208  Utilities->CallLogPop(791);
11209  return false;
11210  }
11211  }
11212  else // check not Snt-sh & carry out successor validity checks for unlocated Snt that isn't a SignallerControl entry
11213  {
11214  if(AVEntry0.Command == "Snt-sh")
11215  {
11216  SecondPassMessage(GiveMessages, "Error in timetable - 'Snt-sh' event not at stop location for: " + TDEntry.HeadCode);
11217  TrainDataVector.clear();
11218  Utilities->CallLogPop(1042);
11219  return false;
11220  }
11221  AVEntry0.LocationType = EnRoute;
11222  if(!AVEntry0.SignallerControl)
11223  {
11224  const TActionVectorEntry &AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
11225  // at least 2 entries checked in integrity check so (1) valid
11226  if(!MovingSuccessor(AVEntry1))
11227  {
11228  SecondPassMessage(GiveMessages, "Error in timetable - unlocated 'Snt' not followed by 'Fer', 'pas' or an arrival for: " +
11229  TDEntry.HeadCode);
11230  TrainDataVector.clear();
11231  Utilities->CallLogPop(790);
11232  return false;
11233  }
11234  }
11235  }
11236  }
11237  // check other start successors
11238  else if(AVEntry0.SequenceType == Start)
11239  {
11240  const TActionVectorEntry &AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
11241  // at least 2 entries present checked in integrity check so (1) valid
11242  if(!AtLocSuccessor(AVEntry1))
11243  {
11244  SecondPassMessage(GiveMessages, "Error in timetable - 'Sfs', 'Sns', 'Sns-sh' or 'Sns-fsh' followed by an illegal event for: " +
11245  TDEntry.HeadCode + ". The event isn't valid for a stationary train.");
11246  TrainDataVector.clear();
11247  Utilities->CallLogPop(793);
11248  return false;
11249  }
11250  }
11251  }
11252 
11253  // set Sfs, Sns, Sns-sh & 'Sns-fsh' locations same as following TimeLoc departure entry location, if no departure before end of sequence give error message
11254  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
11255  {
11256  bool FoundFlag = false;
11257  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11258  TActionVectorEntry & AVEntry = TrainDataVector.at(x).ActionVector.at(0);
11259  // use reference so can change internals
11260  if((AVEntry.Command == "Sfs") || (AVEntry.Command == "Sns") || (AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Sns-fsh"))
11261  {
11262  for(unsigned int y = 1; y < TrainDataVector.at(x).ActionVector.size(); y++)
11263  {
11264  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y);
11265  if(AVEntry2.FormatType == TimeLoc)
11266  {
11267  FoundFlag = true;
11268  AVEntry.LocationName = AVEntry2.LocationName;
11269  break;
11270  }
11271  }
11272  if(!FoundFlag)
11273  {
11274  SecondPassMessage(GiveMessages, "Error in timetable - no location departure following an 'Sfs', 'Sns', 'Sns-sh'or 'Sns-fsh' event for: " +
11275  TDEntry.HeadCode);
11276  TrainDataVector.clear();
11277  Utilities->CallLogPop(851);
11278  return false;
11279  }
11280  }
11281  }
11282 
11283  // set all cmd locations based on earlier location name in TimeLoc arrival or Sfs/Sns/Sns-sh/Sns-fsh/located Snt/Snt-sh locations
11284  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
11285  {
11286  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11287  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11288  {
11289  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11290  if((AVEntry.FormatType == TimeLoc) || ((AVEntry.SequenceType == Start) && (AVEntry.LocationType == AtLocation)))
11291  {
11292  if(AVEntry.LocationName == "")
11293  // if TimeLoc turns out to be a TimeLoc departure then will emerge & be rejected in successor checks for TimeLocs
11294  {
11295  SecondPassMessage(GiveMessages, "Error in timetable for " + TDEntry.HeadCode +
11296  ": an event should have had a location name associated with it but it could not be found");
11297  TrainDataVector.clear();
11298  Utilities->CallLogPop(1831);
11299  return false;
11300  // throw Exception("Error, entry location null in TimeLoc/Sfs/Sns/Sns-sh/Sns-fsh/Snt-sh/located Snt for Train: " + TDEntry.HeadCode);
11301  }
11302  for(unsigned int z = y + 1; z < TrainDataVector.at(x).ActionVector.size(); z++)
11303  {
11304  TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(z);
11305  // use reference so can change internals where necessary
11306  if((AVEntry2.Command != "") && (AVEntry2.LocationType == AtLocation))
11307  {
11308  AVEntry2.LocationName = AVEntry.LocationName;
11309  }
11310  else
11311  break;
11312  }
11313  }
11314  }
11315  }
11316  // all location names now set
11317 
11318  // check remaining successor validity except for TimeLoc arr & dep since those times not set yet
11319  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
11320  {
11321  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11322  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11323  {
11324  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11325  if((AVEntry.SequenceType == Finish) && (AVEntry.Command != "F-nshs"))
11326  {
11327  if(y < (TrainDataVector.at(x).ActionVector.size() - 1))
11328  // i.e at least one more, must be a repeat
11329  {
11330  if(TrainDataVector.at(x).ActionVector.at(y + 1).FormatType != Repeat)
11331  {
11332  SecondPassMessage(GiveMessages, "Error in timetable - only a repeat can follow a finish entry for: " + TDEntry.HeadCode);
11333  TrainDataVector.clear();
11334  Utilities->CallLogPop(798);
11335  return false;
11336  }
11337  }
11338  }
11339  if(AVEntry.Command == "F-nshs")
11340  {
11341  if(y != (TrainDataVector.at(x).ActionVector.size() - 1))
11342  // i.e has to be the last
11343  {
11344  SecondPassMessage(GiveMessages, "Error in timetable - F-nshs (shuttle link) must be the last event for: " + TDEntry.HeadCode);
11345  TrainDataVector.clear();
11346  Utilities->CallLogPop(1049);
11347  return false;
11348  }
11349  }
11350  if(AVEntry.Command == "pas")
11351  {
11352  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
11353  {
11354  SecondPassMessage(GiveMessages, "Error in timetable - a 'pas' can't be the last event for: " + TDEntry.HeadCode);
11355  TrainDataVector.clear();
11356  Utilities->CallLogPop(1518);
11357  return false;
11358  }
11359  }
11360  if(AVEntry.Command == "jbo")
11361  {
11362  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
11363  {
11364  SecondPassMessage(GiveMessages, "Error in timetable - a 'jbo' can't be the last event for: " + TDEntry.HeadCode);
11365  TrainDataVector.clear();
11366  Utilities->CallLogPop(800);
11367  return false;
11368  }
11369  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
11370  if(!AtLocSuccessor(AVEntry2))
11371  {
11372  SecondPassMessage(GiveMessages, "Error in timetable - a jbo event is followed by an illegal event for: " + TDEntry.HeadCode +
11373  ". The event isn't valid for a stationary train.");
11374  TrainDataVector.clear();
11375  Utilities->CallLogPop(801);
11376  return false;
11377  }
11378  }
11379  if((AVEntry.Command == "fsp") || (AVEntry.Command == "rsp"))
11380  {
11381  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
11382  {
11383  SecondPassMessage(GiveMessages, "Error in timetable - a train split can't be the last event for: " + TDEntry.HeadCode);
11384  TrainDataVector.clear();
11385  Utilities->CallLogPop(802);
11386  return false;
11387  }
11388  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
11389  if(!AtLocSuccessor(AVEntry2))
11390  {
11391  SecondPassMessage(GiveMessages, "Error in timetable - a train split is followed by an illegal event for: " + TDEntry.HeadCode +
11392  ". The event isn't valid for a stationary train.");
11393  TrainDataVector.clear();
11394  Utilities->CallLogPop(803);
11395  return false;
11396  }
11397  }
11398  if(AVEntry.Command == "cdt")
11399  {
11400  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
11401  {
11402  SecondPassMessage(GiveMessages, "Error in timetable - a 'cdt' can't be the last event for: " + TDEntry.HeadCode);
11403  TrainDataVector.clear();
11404  Utilities->CallLogPop(804);
11405  return false;
11406  }
11407  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
11408  if(!AtLocSuccessor(AVEntry2))
11409  {
11410  SecondPassMessage(GiveMessages, "Error in timetable - a 'cdt' is followed by an illegal event for: " + TDEntry.HeadCode +
11411  ". The event isn't valid for a stationary train.");
11412  TrainDataVector.clear();
11413  Utilities->CallLogPop(805);
11414  return false;
11415  }
11416  }
11417  if(AVEntry.FormatType == TimeTimeLoc)
11418  {
11419  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
11420  {
11421  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure can't be the last event for: " + TDEntry.HeadCode);
11422  TrainDataVector.clear();
11423  Utilities->CallLogPop(806);
11424  return false;
11425  }
11426  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
11427  if(!MovingSuccessor(AVEntry2))
11428  {
11429  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure is followed by an illegal event for: " +
11430  TDEntry.HeadCode + ". The event isn't valid for a moving train.");
11431  TrainDataVector.clear();
11432  Utilities->CallLogPop(807);
11433  return false;
11434  }
11435  }
11436  if(AVEntry.FormatType == PassTime)
11437  {
11438  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
11439  {
11440  SecondPassMessage(GiveMessages, "Error in timetable - a pass time can't be the last event for: " + TDEntry.HeadCode);
11441  TrainDataVector.clear();
11442  Utilities->CallLogPop(1530);
11443  return false;
11444  }
11445  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
11446  if(!MovingSuccessor(AVEntry2))
11447  {
11448  SecondPassMessage(GiveMessages, "Error in timetable - a pass time is followed by an illegal event for: " + TDEntry.HeadCode +
11449  ". The event isn't valid for a moving train.");
11450  TrainDataVector.clear();
11451  Utilities->CallLogPop(1531);
11452  return false;
11453  }
11454  }
11455  if(AVEntry.FormatType == Repeat)
11456  {
11457  if(y != (TrainDataVector.at(x).ActionVector.size() - 1))
11458  {
11459  SecondPassMessage(GiveMessages, "Error in timetable - a repeat is not the last item for: " + TDEntry.HeadCode);
11460  TrainDataVector.clear();
11461  Utilities->CallLogPop(808);
11462  return false;
11463  }
11464  }
11465  }
11466  }
11467 
11468  // set arrival & departure times for TimeLocs & set their EventTimes to -1
11469  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
11470  {
11471  bool LastEntryIsAnArrival = false;
11472  const TTrainDataEntry & TDEntry = TrainDataVector.at(x);
11473  // first deal with unlocated Snt entries - so next entry (TimeLoc or TimeTimeLoc) is an arrival, all else stopped so the next TimeLoc is a departure
11474  const TActionVectorEntry &AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
11475  if((AVEntry0.Command == "Snt") && (AVEntry0.LocationType == EnRoute))
11476  // StartSpeed may or may not be 0, but train will move forwards (if capable of doing so), & next TimeLoc will be an arrival, whether or not after one or more TimeTimeLocs
11477  {
11478  LastEntryIsAnArrival = false;
11479  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11480  {
11481  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11482  if(AVEntry.FormatType == TimeLoc)
11483  {
11484  if((AVEntry.ArrivalTime > TDateTime(-1)) || (AVEntry.DepartureTime > TDateTime(-1)) || (AVEntry.EventTime == TDateTime(-1)))
11485  {
11486  throw Exception("Timetable error, TimeLoc times not as initially set for " + TDEntry.HeadCode);
11487  }
11488  if(LastEntryIsAnArrival)
11489  {
11490  AVEntry.DepartureTime = AVEntry.EventTime;
11491  AVEntry.EventTime = TDateTime(-1);
11492  LastEntryIsAnArrival = false;
11493  }
11494  else // last entry a departure
11495  {
11496  AVEntry.ArrivalTime = AVEntry.EventTime;
11497  AVEntry.EventTime = TDateTime(-1);
11498  LastEntryIsAnArrival = true;
11499  }
11500  }
11501  }
11502  }
11503  else // all others stopped at beginning
11504  {
11505  LastEntryIsAnArrival = true;
11506  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11507  {
11508  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11509  if(AVEntry.FormatType == TimeLoc)
11510  {
11511  if((AVEntry.ArrivalTime > TDateTime(-1)) || (AVEntry.DepartureTime > TDateTime(-1)) || (AVEntry.EventTime == TDateTime(-1)))
11512  {
11513  throw Exception("Timetable error, TimeLoc times not as initially set for " + TDEntry.HeadCode);
11514  }
11515  if(LastEntryIsAnArrival)
11516  {
11517  AVEntry.DepartureTime = AVEntry.EventTime;
11518  AVEntry.EventTime = TDateTime(-1);
11519  LastEntryIsAnArrival = false;
11520  }
11521  else // last entry a departure
11522  {
11523  AVEntry.ArrivalTime = AVEntry.EventTime;
11524  AVEntry.EventTime = TDateTime(-1);
11525  LastEntryIsAnArrival = true;
11526  }
11527  }
11528  }
11529  }
11530  }
11531  // perform remaining successor checks for TimeLocs
11532  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
11533  {
11534  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11535  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11536  {
11537  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11538  if((AVEntry.FormatType == TimeLoc) && (AVEntry.ArrivalTime >= TDateTime(0))) // arrival
11539  // TimeLoc (arr) -> No starts, repeats, Fer or TimeTimeLoc; TimeLoc (dep) or any other OK
11540  {
11541  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
11542  {
11543  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival can't be the last event for: " + TDEntry.HeadCode);
11544  TrainDataVector.clear();
11545  Utilities->CallLogPop(809);
11546  return false;
11547  }
11548  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
11549  if(!AtLocSuccessor(AVEntry2))
11550  {
11551  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival is followed by an illegal event for: " + TDEntry.HeadCode +
11552  ". The event isn't valid for a stationary train.");
11553  TrainDataVector.clear();
11554  Utilities->CallLogPop(810);
11555  return false;
11556  }
11557  }
11558  if((AVEntry.FormatType == TimeLoc) && (AVEntry.DepartureTime >= TDateTime(0))) // departure
11559  // TimeLoc (dep) -> Fer, TimeLoc (arr), TimeTimeLoc, (new) pas OK, no others
11560  {
11561  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
11562  {
11563  SecondPassMessage(GiveMessages, "Error in timetable - a timed departure can't be the last event for: " + TDEntry.HeadCode);
11564  TrainDataVector.clear();
11565  Utilities->CallLogPop(811);
11566  return false;
11567  }
11568  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
11569  if(!MovingSuccessor(AVEntry2))
11570  {
11571  SecondPassMessage(GiveMessages, "Error in timetable - a timed departure is followed by an illegal event for: " + TDEntry.HeadCode +
11572  ". The event isn't valid for a moving train.");
11573  TrainDataVector.clear();
11574  Utilities->CallLogPop(812);
11575  return false;
11576  }
11577  }
11578  }
11579  }
11580 
11581  // check all TimeLocs have either Arr or Dep time set and EventTime == -1, all Cmds have EventTime set & Arr & Dep times == -1,
11582  // & repeats have no times set
11583  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
11584  {
11585  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11586  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11587  {
11588  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11589  if(AVEntry.FormatType == TimeLoc)
11590  {
11591  if(AVEntry.EventTime != TDateTime(-1))
11592  {
11593  throw Exception("Timetable error, TimeLoc entry has EventTime not -1 for " + TDEntry.HeadCode);
11594  }
11595  if((AVEntry.ArrivalTime == TDateTime(-1)) && (AVEntry.DepartureTime == TDateTime(-1)))
11596  {
11597  throw Exception("Timetable error, TimeLoc entry has neither arrival nor departure time set for " + TDEntry.HeadCode);
11598  }
11599  }
11600  if(AVEntry.FormatType == TimeTimeLoc)
11601  {
11602  if(AVEntry.EventTime != TDateTime(-1))
11603  {
11604  throw Exception("Timetable error, TimeTimeLoc entry has EventTime not -1 for " + TDEntry.HeadCode);
11605  }
11606  if((AVEntry.ArrivalTime == TDateTime(-1)) || (AVEntry.DepartureTime == TDateTime(-1)))
11607  {
11608  throw Exception("Timetable error, TimeTimeLoc entry has either arrival or departure time not set for " + TDEntry.HeadCode);
11609  }
11610  }
11611  if((AVEntry.FormatType == TimeCmd) || (AVEntry.FormatType == TimeCmdHeadCode) || (AVEntry.FormatType == StartNew) ||
11612  (AVEntry.FormatType == SNTShuttle) || (AVEntry.FormatType == SNSShuttle) || (AVEntry.FormatType == FNSNonRepeatToShuttle) ||
11613  (AVEntry.FormatType == FSHNewService) || (AVEntry.FormatType == PassTime))
11614  {
11615  if(AVEntry.EventTime == TDateTime(-1))
11616  {
11617  throw Exception("Timetable error, Cmd or PassTime entry has EventTime not set for " + TDEntry.HeadCode);
11618  }
11619  if((AVEntry.ArrivalTime != TDateTime(-1)) || (AVEntry.DepartureTime != TDateTime(-1)))
11620  {
11621  throw Exception("Timetable error, Cmd or PassTime entry has either arrival or departure time set for " + TDEntry.HeadCode);
11622  }
11623  }
11624  if(AVEntry.FormatType == Repeat)
11625  {
11626  if((AVEntry.EventTime != TDateTime(-1)) || (AVEntry.ArrivalTime != TDateTime(-1)) || (AVEntry.DepartureTime != TDateTime(-1)))
11627  {
11628  throw Exception("Timetable error, Repeat entry has a time set for " + TDEntry.HeadCode);
11629  }
11630  }
11631  }
11632  }
11633 
11634  // check times stay same or increase, note that can have time of 0 if include midnight
11635  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
11636  {
11637  TDateTime CurrentTime = TTClockTime; // the timetable start time
11638  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11639  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11640  {
11641  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11642  if(AVEntry.FormatType == Repeat)
11643  break;
11644  if(AVEntry.FormatType == FinRemHere)
11645  break;
11646  if(AVEntry.FormatType == TimeTimeLoc)
11647  {
11648  if(AVEntry.DepartureTime < AVEntry.ArrivalTime)
11649  {
11650  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure has a later arrival than departure time for: " +
11651  TDEntry.HeadCode);
11652  TrainDataVector.clear();
11653  Utilities->CallLogPop(813);
11654  return false;
11655  }
11656  if(AVEntry.ArrivalTime < CurrentTime)
11657  {
11658  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure has too early an arrival time for: " +
11659  TDEntry.HeadCode);
11660  TrainDataVector.clear();
11661  Utilities->CallLogPop(814);
11662  return false;
11663  }
11664  CurrentTime = AVEntry.DepartureTime;
11665  continue;
11666  }
11667  if(AVEntry.FormatType == TimeLoc)
11668  {
11669  if(AVEntry.ArrivalTime >= TDateTime(0))
11670  {
11671  if(AVEntry.ArrivalTime < CurrentTime)
11672  {
11673  SecondPassMessage(GiveMessages, "Error in timetable - a timed location event has a time that is too early for: " + TDEntry.HeadCode);
11674  TrainDataVector.clear();
11675  Utilities->CallLogPop(815);
11676  return false;
11677  }
11678  CurrentTime = AVEntry.ArrivalTime;
11679  }
11680  else
11681  {
11682  if(AVEntry.DepartureTime < CurrentTime)
11683  // both may be 0 legitimately so must allow for this
11684  {
11685  SecondPassMessage(GiveMessages, "Error in timetable - a timed location event has a time that is too early for: " + TDEntry.HeadCode);
11686  TrainDataVector.clear();
11687  Utilities->CallLogPop(816);
11688  return false;
11689  }
11690  CurrentTime = AVEntry.DepartureTime;
11691  }
11692  continue;
11693  }
11694  if(AVEntry.EventTime < CurrentTime)
11695  // all others have EventTime set
11696  {
11697  SecondPassMessage(GiveMessages, "Error in timetable - a train event has a time that is set too early for: " + TDEntry.HeadCode +
11698  ", may be before timetable start time");
11699  TrainDataVector.clear();
11700  Utilities->CallLogPop(835);
11701  return false;
11702  }
11703  CurrentTime = AVEntry.EventTime;
11704  continue;
11705  }
11706  }
11707 
11708  // check locations consistent
11709  AnsiString LastLocationName = "";
11710 
11711  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
11712  {
11713  bool LastEntryIsAnArrival = false;
11714  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11715  // first deal with moving Snt entries (all else stopped)
11716  if((TrainDataVector.at(x).ActionVector.at(0).Command == "Snt") && (TrainDataVector.at(x).ActionVector.at(0).LocationType == EnRoute))
11717  {
11718  LastEntryIsAnArrival = false;
11719  LastLocationName = TrainDataVector.at(x).ActionVector.at(0).LocationName; // should be ""
11720  if(LastLocationName != "")
11721  {
11722  throw Exception("Timetable error, moving Snt entry has LocationName set for " + TDEntry.HeadCode);
11723  }
11724  for(unsigned int y = 1; y < TrainDataVector.at(x).ActionVector.size();
11725  y++) // note that immediate successor to a moving Snt can only be a Moving type
11726  // if it's a SignallerControl entry then the condition isn't met
11727  {
11728  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11729  if(AVEntry.FormatType == Repeat)
11730  break; // repeat = reached end (+allows repeat after signaller controlled entry)
11731  else if((AVEntry.FormatType == TimeCmdHeadCode) || (AVEntry.FormatType == FNSNonRepeatToShuttle))
11732  {
11733  if(AVEntry.LocationName != LastLocationName)
11734  {
11735  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
11736  AVEntry.Command);
11737  TrainDataVector.clear();
11738  Utilities->CallLogPop(823);
11739  return false;
11740  }
11741  }
11742  else if(AVEntry.FormatType == TimeCmd)
11743  // cdt is the only TimeCmd
11744  {
11745  if(AVEntry.LocationName != LastLocationName)
11746  {
11747  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
11748  AVEntry.Command);
11749  TrainDataVector.clear();
11750  Utilities->CallLogPop(824);
11751  return false;
11752  }
11753  }
11754  else if(AVEntry.FormatType == TimeTimeLoc)
11755  {
11756  if((AVEntry.LocationName == LastLocationName) && !TwoOrMoreLocationsWarningGiven && TTEditPanelVisible) //changed at v2.6.0 to allow loops & consecutive same locs
11757  // last entry must be a departure or would have failed earlier
11758  {
11759  ShowMessage("Two or more locations are the same without a change of direction between them. Please correct if this is an error.\n\nThis warning will not be shown again.");
11761 // SecondPassMessage(GiveMessages,
11762 // "Error in timetable - a location event in a timed arrival and departure is the same as the last location for: " + TDEntry.HeadCode);
11763 // TrainDataVector.clear();
11764 // Utilities->CallLogPop(825);
11765 // return false;
11766  }
11767  LastLocationName = AVEntry.LocationName;
11768  LastEntryIsAnArrival = false;
11769  }
11770  else if(AVEntry.FormatType == TimeLoc)
11771  {
11772  if(LastEntryIsAnArrival && (AVEntry.LocationName != LastLocationName))
11773  {
11774  SecondPassMessage(GiveMessages,
11775  "Error in timetable - a location event for a timed departure is different from the arrival location for: " + TDEntry.HeadCode);
11776  TrainDataVector.clear();
11777  Utilities->CallLogPop(826);
11778  return false;
11779  }
11780  else if(!LastEntryIsAnArrival && (AVEntry.LocationName == LastLocationName))
11781  {
11782  SecondPassMessage(GiveMessages,
11783  "Error in timetable - a location event for a timed arrival is the same as the earlier departure location for: " + TDEntry.HeadCode);
11784  TrainDataVector.clear();
11785  Utilities->CallLogPop(827);
11786  return false;
11787  }
11788  LastLocationName = AVEntry.LocationName;
11789  LastEntryIsAnArrival = !LastEntryIsAnArrival;
11790  }
11791  }
11792  }
11793  else // all stationary starting entries
11794  {
11795  LastEntryIsAnArrival = true;
11796  LastLocationName = TrainDataVector.at(x).ActionVector.at(0).LocationName;
11797  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11798  {
11799  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11800  if(AVEntry.FormatType == Repeat)
11801  break;
11802  else if((AVEntry.FormatType == TimeCmdHeadCode) || (AVEntry.FormatType == FNSNonRepeatToShuttle))
11803  // no need to add anything for shuttle starts since they are at loc (0) anyway
11804  {
11805  if(AVEntry.LocationName != LastLocationName)
11806  {
11807  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
11808  AVEntry.Command);
11809  TrainDataVector.clear();
11810  Utilities->CallLogPop(828);
11811  return false;
11812  }
11813  }
11814  else if(AVEntry.FormatType == TimeCmd)
11815  // cdt is the only TimeCmd
11816  {
11817  if(AVEntry.LocationName != LastLocationName)
11818  {
11819  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
11820  AVEntry.Command);
11821  TrainDataVector.clear();
11822  Utilities->CallLogPop(829);
11823  return false;
11824  }
11825  }
11826  else if(AVEntry.FormatType == TimeTimeLoc)
11827  {
11828  if((AVEntry.LocationName == LastLocationName) && !TwoOrMoreLocationsWarningGiven && TTEditPanelVisible) //changed at v2.6.0 to allow loops & consecutive same locs
11829  // last entry must be a departure or would have failed earlier
11830  {
11831  ShowMessage("Two or more locations are the same without a change of direction between them. Please correct if this is an error.\n\nThis warning will not be shown again.");
11833 // SecondPassMessage(GiveMessages,
11834 // "Error in timetable - a location event in a timed arrival and departure is the same as the last location for: " + TDEntry.HeadCode);
11835 // TrainDataVector.clear();
11836 // Utilities->CallLogPop(830);
11837 // return false;
11838  }
11839  LastLocationName = AVEntry.LocationName;
11840  LastEntryIsAnArrival = false;
11841  }
11842  else if(AVEntry.FormatType == TimeLoc)
11843  {
11844  if(LastEntryIsAnArrival && (AVEntry.LocationName != LastLocationName))
11845  {
11846  SecondPassMessage(GiveMessages,
11847  "Error in timetable - a location event for a timed departure is different from the arrival location for: " + TDEntry.HeadCode);
11848  TrainDataVector.clear();
11849  Utilities->CallLogPop(831);
11850  return false;
11851  }
11852  if(!LastEntryIsAnArrival && (AVEntry.LocationName == LastLocationName) && !TwoOrMoreLocationsWarningGiven)
11853  {
11854  SecondPassMessage(GiveMessages,
11855  "A location event for a timed arrival is the same as the earlier departure location for: " + TDEntry.HeadCode + ". Please correct if this is an error.\n\nThis warning will not be shown again.");
11857 // TrainDataVector.clear();
11858 // Utilities->CallLogPop(832);
11859 // return false;
11860  }
11861  LastLocationName = AVEntry.LocationName;
11862  LastEntryIsAnArrival = !LastEntryIsAnArrival;
11863  }
11864  }
11865  }
11866  }
11867 
11868  // Check same location doesn't appear twice before a cdt except for separate arr & dep TimeLocs (just a potential error warning given in v2.6.0)
11869  // i.e. same location can appear in any number of consecutive entries but once changed couldn't repeat before a direction change prior to v2.6.0
11870  AnsiString LocationNameToBeChecked = "";
11871 
11872  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
11873  {
11874  const TTrainDataEntry & TDEntry = TrainDataVector.at(x);
11875  unsigned int y = 0;
11876  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
11877  // first discard unlocated Snt entries as they don't have location name set
11878  if((AVEntry0.Command == "Snt") && (AVEntry0.LocationType == EnRoute))
11879  {
11880  y = 1;
11881  }
11882  while(y < TDEntry.ActionVector.size())
11883  // need to check each location name separately in turn, skipped for SignallerControl entries
11884  // if y == 1
11885  {
11886  if((TDEntry.ActionVector.at(y).Command == "Fer") || (TDEntry.ActionVector.at(y).FormatType == Repeat))
11887  {
11888  break; // out of the 'while' loop since have reached the end & 'Fer' & 'Repeat' have no location name set
11889  }
11890  LocationNameToBeChecked = TDEntry.ActionVector.at(y).LocationName;
11891  for(unsigned int z = y; z < TDEntry.ActionVector.size(); z++)
11892  {
11893  const TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(z);
11894  if((AVEntry.Command == "Fer") || (AVEntry.FormatType == Repeat))
11895  {
11896  break; // out of the 'z' loop since have reached the end & 'Fer' & 'Repeat' have no location name set
11897  }
11898  if(AVEntry.Command == "cdt")
11899  {
11900  break; // out of the 'z' loop since the check is only valid up to a change of direction
11901  }
11902  if(AVEntry.LocationName == LocationNameToBeChecked)
11903  continue; // keep going while name same
11904  if(AVEntry.LocationName != LocationNameToBeChecked)
11905  // if name different check forwards to see if repeats
11906  {
11907  for(unsigned int a = z; a < TDEntry.ActionVector.size(); a++)
11908  {
11909  if(TDEntry.ActionVector.at(a).Command == "cdt")
11910  {
11911  break; // out of the 'a' & 'z' loops since the check is only valid up to a change of direction
11912  }
11913  if((TDEntry.ActionVector.at(a).LocationName == LocationNameToBeChecked) && !TwoOrMoreLocationsWarningGiven && TTEditPanelVisible) //changed at v2.6.0 to allow loops & consecutive same locs
11914  {
11915  ShowMessage("Two or more locations are the same without a change of direction between them. Please correct if this is an error.\n\nThis warning will not be shown again.");
11917  // SecondPassMessage(GiveMessages, "Error in timetable - a location entry appears twice inappropriately for: " + TDEntry.HeadCode);
11918  // TrainDataVector.clear();
11919  // Utilities->CallLogPop(833);
11920  // return false;
11921  }
11922  }
11923  break; // out of the 'z' loop since have checked 'a' as far as need to
11924  }
11925  }
11926  y++;
11927  }
11928  }
11929 
11930  // check all locations except unlocated 'Snt' & 'Fer' have LocationName set
11931  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
11932  {
11933  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11934  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11935  {
11936  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11937  if((AVEntry.LocationName == "") && (AVEntry.Command != "Snt") && (AVEntry.Command != "Fer") && (AVEntry.FormatType != Repeat))
11938  {
11939  throw Exception("Error, non- 'Snt', 'Fer' or Repeat entry doesn't have a location name set for " + TDEntry.HeadCode);
11940  }
11941  AnsiString LocName = "";
11942  // dummy, only used so can call IsSNTEntryLocated
11943  if((AVEntry.Command == "Snt") && (IsSNTEntryLocated(1, TrainDataVector.at(x), LocName)))
11944  {
11945  if(AVEntry.LocationName == "")
11946  {
11947  throw Exception("Error, 'Snt' entry at a stop location doesn't have a location name set for " + TDEntry.HeadCode);
11948  }
11949  }
11950  if((AVEntry.Command == "Snt") && !(IsSNTEntryLocated(2, TrainDataVector.at(x), LocName)))
11951  {
11952  if(AVEntry.LocationName != "")
11953  {
11954  throw Exception("Error, 'Snt' unlocated entry has a location name set for " + TDEntry.HeadCode);
11955  }
11956  }
11957  }
11958  }
11959 
11960 /* Check a split to 'x' doesn't again split to 'x' (anywhere, not just for one train, since headcodes can be duplicated)
11961  Check each joined by train not joined by same train again (anywhere, not just for one train, since headcodes can be duplicated)
11962  Check each change of headcode not repeated anywhere else (anywhere, not just for one train, since headcodes can be duplicated)
11963 
11964  i.e. check everywhere where there is an 'OtherHeadCode' that it matches once only with its reference (both ways) + set
11965  the OtherHeadCodeStartingEntryPtr pointers where appropriate + train information for splits & new services
11966 
11967  BUT need to separate the shuttles from non-shuttles, because can have two trains reference each other in both forms,
11968  eg 2F44 Sns-sh ends in Fns to 2F45, & Sns 2F45 ends in Fns-sh to 2F44. Here 2F45 is the 'OtherHeadCode' for both
11969  Sns-sh & Fns in train 2F44, & 2F44 is the 'OtherHeadCode' for both Sns & Fns-sh in train 2F45.
11970 */
11971  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // new test to ensure no duplicate links at all, other checks ensure none for shuttles,
11972  // non-shuttles & non-repeating links separately, but don't check that there isn't a
11973  // duplicate between a non-repeating shuttle and another - leave original tests in as
11974  // these also set the pointers
11975  {
11976  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11977  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11978  {
11979  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11980  if(AVEntry.OtherHeadCode != "")
11981  {
11982  if(!CheckForDuplicateCrossReferences(0, TDEntry.HeadCode, AVEntry.OtherHeadCode, GiveMessages))
11983  {
11984  Utilities->CallLogPop(1584);
11985  return false; // error message given in called function
11986  }
11987  }
11988  if(AVEntry.NonRepeatingShuttleLinkHeadCode != "")
11989  {
11990  if(!CheckForDuplicateCrossReferences(1, TDEntry.HeadCode, AVEntry.NonRepeatingShuttleLinkHeadCode, GiveMessages))
11991  {
11992  Utilities->CallLogPop(1585);
11993  return false; // error message given in called function
11994  }
11995  }
11996  }
11997  }
11998 
11999  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12000  {
12001  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12002  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12003  {
12004  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12005  if((AVEntry.Command != "Sns-sh") && (AVEntry.Command != "Snt-sh") && (AVEntry.Command != "Fns-sh") && (AVEntry.Command != "Frh-sh"))
12006  {
12007  if(AVEntry.OtherHeadCode != "")
12008  {
12009  if(!CheckCrossReferencesAndSetData(0, TDEntry.HeadCode, AVEntry.OtherHeadCode, false, GiveMessages))
12010  // false = non-shuttle
12011  {
12012  Utilities->CallLogPop(864);
12013  return false; // error message given in called function
12014  }
12015  }
12016  }
12017  }
12018  }
12019 
12020  // now repeat the check just for the shuttles
12021  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12022  {
12023  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12024  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12025  {
12026  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12027  if((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh") || (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "Frh-sh"))
12028  {
12029  if(AVEntry.OtherHeadCode != "")
12030  {
12031  if(!CheckCrossReferencesAndSetData(1, TDEntry.HeadCode, AVEntry.OtherHeadCode, true, GiveMessages))
12032  // true = shuttle
12033  {
12034  Utilities->CallLogPop(1100);
12035  return false; // error message given in called function
12036  }
12037  }
12038  }
12039  }
12040  }
12041 
12042  // check for proper non-repeating link cross references and that they have no repeats & that times are consistent
12043  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12044  {
12045  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12046  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12047  {
12048  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12049  if(AVEntry.NonRepeatingShuttleLinkHeadCode != "")
12050  {
12052  {
12053  Utilities->CallLogPop(1060);
12054  return false; // error message given in called function
12055  }
12056  }
12057  }
12058  }
12059 
12060  // check that each shuttle start ends either in Fns or Fxx-sh (though a single service can't end in Fxx-sh), and that
12061  // when the Fxx-sh is reached it references the original start and not another shuttle - not allowed to link two shuttles,
12062  // don't ever need to and as designed would skip repeats
12063  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12064  {
12065  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12066  {
12067  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12068  if((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh"))
12069  {
12070  if(!CheckShuttleServiceIntegrity(0, &(TrainDataVector.at(x)), GiveMessages))
12071  {
12072  Utilities->CallLogPop(1090);
12073  return false; // error message given in called function
12074  }
12075  }
12076  }
12077  }
12078 
12079  // check all entries have all types set to something
12080  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12081  {
12082  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12083  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12084  {
12085  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12086  if(AVEntry.FormatType == NoFormat)
12087  {
12088  throw Exception("Error - timetable ActionVector entry no. " + AnsiString(y) + " has FormatType unset for: " + TDEntry.HeadCode);
12089  }
12090  else if(AVEntry.SequenceType == NoSequence)
12091  {
12092  throw Exception("Error - timetable ActionVector entry no. " + AnsiString(y) + " has SequenceType unset for: " + TDEntry.HeadCode);
12093  }
12094  else if(AVEntry.LocationType == NoLocation)
12095  {
12096  throw Exception("Error - timetable ActionVector entry no. " + AnsiString(y) + " has LocationType unset for: " + TDEntry.HeadCode);
12097  }
12098  else if(AVEntry.ShuttleLinkType == NoShuttleLink)
12099  {
12100  throw Exception("Error - timetable ActionVector entry no. " + AnsiString(y) + " has ShuttleLinkType unset for: " + TDEntry.HeadCode);
12101  }
12102  }
12103  }
12104 
12105  // all OK if reach here, so set up the TrainOperatingDataVector (already has one entry) & NumberOfTrains
12106  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12107  {
12108  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
12109  // non-const reference so can alter content
12110  TTrainOperatingData TData;
12111  const TActionVectorEntry &LastAVEntry = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
12112  if(LastAVEntry.FormatType == Repeat) // check if a repeat
12113  {
12114 /*
12115  class TTrainOperatingData
12116  {
12117  public:
12118  int TrainID; - default, set at construction
12119  TActionEventType EventReported; used during operation
12120  TRunningEntry RunningEntry; - default, set at construction
12121  TTrainOperatingData() {TrainID = -1; EventReported= NoEvent; RunningEntry=NotStarted;} //constructor, values set to defaults
12122  };
12123 */
12124  TDEntry.NumberOfTrains = LastAVEntry.NumberOfRepeats + 1;
12125  for(int y = 1; y < TDEntry.NumberOfTrains; y++)
12126  {
12127  TDEntry.TrainOperatingDataVector.push_back(TData);
12128  }
12129  }
12130  else
12131  TDEntry.NumberOfTrains = 1;
12132  }
12133 
12134  // check that don't include any Continuation names
12135  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12136  {
12137  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12138  {
12139  AnsiString LocName = TrainDataVector.at(x).ActionVector.at(y).LocationName;
12140  AnsiString HC = TrainDataVector.at(x).HeadCode;
12141  if(LocName != "")
12142  {
12143  if(Track->ContinuationNameMap.find(LocName) != Track->ContinuationNameMap.end())
12144  {
12145  SecondPassMessage(GiveMessages, "Error in timetable - continuation names (" + LocName + ") must not be included, see service " + HC);
12146  TrainDataVector.clear();
12147  Utilities->CallLogPop(1578);
12148  return false;
12149  }
12150  }
12151  }
12152  }
12153 
12154  // check that all repeat times below 96h
12155  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12156  {
12157  int NumRepeats = (TrainDataVector.at(x).NumberOfTrains) - 1;
12158  int IncMinutes = 0;
12159  if(TrainDataVector.at(x).ActionVector.at(TrainDataVector.at(x).ActionVector.size() - 1).FormatType == Repeat)
12160  {
12161  IncMinutes = TrainDataVector.at(x).ActionVector.at(TrainDataVector.at(x).ActionVector.size() - 1).RearStartOrRepeatMins;
12162  }
12163  else
12164  continue; // basic times already checked in CheckTimeValidity
12165  AnsiString HC = TrainDataVector.at(x).HeadCode;
12166  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12167  {
12168  if((double)TrainDataVector.at(x).ActionVector.at(y).EventTime > -1)
12169  {
12170  if(((double)GetRepeatTime(32, TrainDataVector.at(x).ActionVector.at(y).EventTime, NumRepeats, IncMinutes) >= 3.9994))
12171  {
12172  SecondPassMessage(GiveMessages, "Error in timetable - a repeat time exceeds 95h 59m, see service " + HC); // 3d 23h 59m = 3.9993055556
12173  TrainDataVector.clear();
12174  Utilities->CallLogPop(1818);
12175  return false;
12176  }
12177  }
12178  if((double)TrainDataVector.at(x).ActionVector.at(y).ArrivalTime > -1)
12179  {
12180  if(((double)GetRepeatTime(33, TrainDataVector.at(x).ActionVector.at(y).EventTime, NumRepeats, IncMinutes) >= 3.9994))
12181  // 3d 23h 59m = 3.9993055556
12182  {
12183  SecondPassMessage(GiveMessages, "Error in timetable - a repeat entry time exceeds 95h 59m, see service " + HC);
12184  TrainDataVector.clear();
12185  Utilities->CallLogPop(1819);
12186  return false;
12187  }
12188  }
12189  if((double)TrainDataVector.at(x).ActionVector.at(y).DepartureTime > -1)
12190  {
12191  if(((double)GetRepeatTime(34, TrainDataVector.at(x).ActionVector.at(y).EventTime, NumRepeats, IncMinutes) >= 3.9994))
12192  // 3d 23h 59m = 3.9993055556
12193  {
12194  SecondPassMessage(GiveMessages, "Error in timetable - a repeat entry time exceeds 95h 59m, see service " + HC);
12195  TrainDataVector.clear();
12196  Utilities->CallLogPop(1820);
12197  return false;
12198  }
12199  }
12200  }
12201  }
12202 
12203  // Now that all set up change any extended headcodes back to ordinary headcodes
12204  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12205  {
12206  StripExcessFromHeadCode(0, TrainDataVector.at(x).HeadCode);
12207  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12208  {
12209  StripExcessFromHeadCode(1, TrainDataVector.at(x).ActionVector.at(y).OtherHeadCode);
12210  StripExcessFromHeadCode(2, TrainDataVector.at(x).ActionVector.at(y).NonRepeatingShuttleLinkHeadCode);
12211  }
12212  }
12213 
12214  // SaveTrainDataVectorToFile(0);//test
12216  Utilities->CallLogPop(782);
12217  return true;
12218 }
12219 
12220 // ---------------------------------------------------------------------------
12221 // Moving successors: TimeLoc arr/TimeTimeLoc/pas/Fer;
12223 {
12224  return ((AVEntry.FormatType == TimeLoc) || (AVEntry.FormatType == TimeTimeLoc) || (AVEntry.Command == "pas") || (AVEntry.Command == "Fer"));
12225 }
12226 
12227 // ---------------------------------------------------------------------------
12228 // AtLoc successors: TimeLoc dep/jbo/fsp/rsp/cdt/Frh/Fns/Fjo/Frh-sh/Fns-sh/F-nshs;
12230 {
12231  return ((AVEntry.FormatType == TimeLoc) || (AVEntry.Command == "jbo") || (AVEntry.Command == "fsp") || (AVEntry.Command == "rsp") ||
12232  (AVEntry.Command == "cdt") || (AVEntry.Command == "Frh") || (AVEntry.Command == "Fns") || (AVEntry.Command == "Fjo") || (AVEntry.Command == "Frh-sh") ||
12233  (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "F-nshs"));
12234 }
12235 
12236 // ---------------------------------------------------------------------------
12237 
12238 void TTrainController::StripExcessFromHeadCode(int Caller, AnsiString &HeadCode)
12239 {
12240  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",StripExcessFromHeadCode," + HeadCode);
12241  if(HeadCode.Length() > 4) // ignore otherwise
12242  {
12243  HeadCode = HeadCode.SubString(HeadCode.Length() - 3, 4);
12244  }
12245  Utilities->CallLogPop(1593);
12246 }
12247 
12248 // ---------------------------------------------------------------------------
12249 
12250 bool TTrainController::CheckForDuplicateCrossReferences(int Caller, AnsiString MainHeadCode, AnsiString SecondHeadCode, bool GiveMessages)
12251 {
12252  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckForDuplicateCrossReferences," + MainHeadCode + "," +
12253  SecondHeadCode);
12254  int ForwardCount = 0;
12255  int ReverseCount = 0;
12256 
12257  if(MainHeadCode == SecondHeadCode)
12258  {
12259  SecondPassMessage(GiveMessages, "Error in timetable - Service " + MainHeadCode + " has an event that references itself");
12260  TrainDataVector.clear();
12261  Utilities->CallLogPop(1594);
12262  return false;
12263  }
12264 
12265  // forward check
12266  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12267  {
12268  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12269  if(TDEntry.HeadCode == MainHeadCode)
12270  {
12271  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12272  {
12273  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12274  if(AVEntry.OtherHeadCode == SecondHeadCode)
12275  {
12276  ForwardCount++;
12277  }
12278  if(AVEntry.NonRepeatingShuttleLinkHeadCode == SecondHeadCode)
12279  // need own check in case both 'Other' & 'NonRepeating' have same headcode
12280  {
12281  ForwardCount++;
12282  }
12283  }
12284  }
12285  }
12286  if(ForwardCount == 0)
12287  // this is an exception because the headcodes are selected in the same order as the forward check
12288  {
12289  throw Exception("Error, ForwardCount == 0 in CheckForDuplicateCrossReferences after called with found values");
12290  }
12291 
12292  if(ForwardCount > 2)
12293  // can have 2 if one is Sns-sh linking from another leg of the shuttle, and Fns links out to that same leg
12294  {
12295  SecondPassMessage(GiveMessages, "Error in timetable - found more than two references to " + SecondHeadCode + " from a train whose headcode is " +
12296  MainHeadCode + ". Check the service cross references from each service, and check whether one or other service is listed twice or more.");
12297  TrainDataVector.clear();
12298  Utilities->CallLogPop(1587);
12299  return false;
12300  }
12301 
12302  // reverse check
12303  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12304  {
12305  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12306  if(TDEntry.HeadCode == SecondHeadCode)
12307  {
12308  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12309  {
12310  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12311  if(AVEntry.OtherHeadCode == MainHeadCode)
12312  {
12313  ReverseCount++;
12314  }
12315  if(AVEntry.NonRepeatingShuttleLinkHeadCode == MainHeadCode)
12316  {
12317  ReverseCount++;
12318  }
12319  }
12320  }
12321  }
12322 
12323  if(ReverseCount == 0)
12324  {
12325  SecondPassMessage(GiveMessages, "Error in timetable - cross reference missing in either " + MainHeadCode + " or " + SecondHeadCode);
12326  TrainDataVector.clear();
12327  Utilities->CallLogPop(1588);
12328  return false;
12329  }
12330  if(ReverseCount > 2)
12331  // can have 2 if one is a second shuttle leg with a link in from Fns, and it links out to the same service with Fxx-sh
12332  {
12333  SecondPassMessage(GiveMessages, "Error in timetable - found more than two references to " + MainHeadCode + " from a train whose headcode is " +
12334  SecondHeadCode + ". Check the service cross references from each service, and check whether one or other service is listed twice or more.");
12335  TrainDataVector.clear();
12336  Utilities->CallLogPop(1589);
12337  return false;
12338  }
12339 
12340  if(ForwardCount != ReverseCount)
12341  {
12342  SecondPassMessage(GiveMessages, "Error in timetable - " + MainHeadCode + " has a different number of references to " + SecondHeadCode +
12343  " than the other way round");
12344  TrainDataVector.clear();
12345  Utilities->CallLogPop(1610);
12346  return false;
12347  }
12348 
12349  Utilities->CallLogPop(1590);
12350  return true;
12351 }
12352 
12353 // ---------------------------------------------------------------------------
12354 
12355 bool TTrainController::CheckCrossReferencesAndSetData(int Caller, AnsiString MainHeadCode, AnsiString OtherHeadCode, bool Shuttle, bool GiveMessages)
12356 /* Return false for no find or more than one find, check correct types of link
12357  First run through all trains whose headcode is the MainHeadCode (may be > 1) & for each entry whose
12358  'other' is OtherHeadCode increment a forward counter. Keep a pointer to the 'OtherHeadCode' entry for use later
12359  Must be exactly 1 forward count. NB Forward relates to MainHeadCode
12360  Then do the same in reverse.
12361  Using the pointers check the event times, then check that the locations & commands match - if main is a split then other must be Sfs;
12362  if main is Fns other must be Sns; if main is jbo other must be Fjo.
12363  Also check platform lengths OK for a split location (call to Track function for this - at least one platform at location has to be long
12364  enough). If all succeeds so far set the relevant OtherHeadCodeStartingEntryPtr to the new service starting point + train information
12365  for Sfs & Sns services. Finally check the repeat entries if present are consistent
12366 
12367  Check all except the NonRepeatingShuttleLinkHeadCodes, which only occur from F-nshs to Sns-sh, and from Fns-sh to
12368  Sns-fsh. All others should check out OK, but check shuttles & non-shuttles separately.
12369 
12370  /NB prohibit main & other headcodes being same, causes probs in failing to recognise locations
12371 */
12372 
12373 {
12374  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckCrossReferencesAndSetData," + MainHeadCode + "," + OtherHeadCode);
12375  int ForwardCount = 0;
12376  int ReverseCount = 0;
12377  unsigned int ForwardTDVectorNumber, ReverseTDVectorNumber;
12378  TActionVectorEntry *ReverseEntryPtr = 0, *ForwardEntryPtr = 0;
12379  TTrainDataEntry *MainTrainDataPtr = 0;
12380  TTrainDataEntry *OtherTrainDataPtr = 0;
12381 
12382  // forward check
12383  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12384  {
12385  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12386  if(TDEntry.HeadCode == MainHeadCode)
12387  {
12388  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12389  {
12390  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12391  if(!Shuttle && (AVEntry.Command != "Sns-sh") && (AVEntry.Command != "Snt-sh") && (AVEntry.Command != "Fns-sh") && (AVEntry.Command != "Frh-sh"))
12392  {
12393  if(AVEntry.OtherHeadCode == OtherHeadCode)
12394  {
12395  MainTrainDataPtr = &TrainDataVector.at(x);
12396  ForwardEntryPtr = &AVEntry;
12397  ForwardCount++;
12398  ForwardTDVectorNumber = x;
12399  }
12400  }
12401  else if(Shuttle && ((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh") || (AVEntry.Command == "Fns-sh") ||
12402  (AVEntry.Command == "Frh-sh")))
12403  {
12404  if(AVEntry.OtherHeadCode == OtherHeadCode)
12405  {
12406  MainTrainDataPtr = &TrainDataVector.at(x);
12407  ForwardEntryPtr = &AVEntry;
12408  ForwardCount++;
12409  ForwardTDVectorNumber = x;
12410  }
12411  }
12412  }
12413  }
12414  }
12415  if(ForwardCount == 0)
12416  // this is an exception because the headcodes are selected in the same order as the forward check
12417  {
12418  throw Exception("Error, ForwardCount == 0 in CheckCrossReferencesAndSetData after called with found values");
12419  }
12420  if(ForwardCount > 1)
12421  {
12422  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + OtherHeadCode + " from a train whose headcode is " +
12423  MainHeadCode);
12424  TrainDataVector.clear();
12425  Utilities->CallLogPop(836);
12426  return false;
12427  }
12428 
12429  // reverse check
12430  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12431  {
12432  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12433  if(TDEntry.HeadCode == OtherHeadCode)
12434  {
12435  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12436  {
12437  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12438  if(!Shuttle && (AVEntry.Command != "Sns-sh") && (AVEntry.Command != "Snt-sh") && (AVEntry.Command != "Fns-sh") && (AVEntry.Command != "Frh-sh"))
12439  {
12440  if(AVEntry.OtherHeadCode == MainHeadCode)
12441  {
12442  OtherTrainDataPtr = &TrainDataVector.at(x);
12443  ReverseCount++;
12444  ReverseEntryPtr = &AVEntry;
12445  ReverseTDVectorNumber = x;
12446  }
12447  }
12448  else if(Shuttle && ((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh") || (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "Frh-sh")))
12449  {
12450  if(AVEntry.OtherHeadCode == MainHeadCode)
12451  {
12452  OtherTrainDataPtr = &TrainDataVector.at(x);
12453  ReverseCount++;
12454  ReverseEntryPtr = &AVEntry;
12455  ReverseTDVectorNumber = x;
12456  }
12457  }
12458  }
12459  }
12460  }
12461 
12462  if(ReverseCount == 0)
12463  {
12464  SecondPassMessage(GiveMessages, "Error in timetable - cross reference missing in either " + MainHeadCode + " or " + OtherHeadCode);
12465  TrainDataVector.clear();
12466  Utilities->CallLogPop(837);
12467  return false;
12468  }
12469  if(ReverseCount > 1)
12470  {
12471  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + MainHeadCode + " from a train whose headcode is " +
12472  OtherHeadCode);
12473  TrainDataVector.clear();
12474  Utilities->CallLogPop(838);
12475  return false;
12476  }
12477 
12478  // these will all be false for !Shuttle
12479  bool ForwardShuttleStart = ((ForwardEntryPtr->Command == "Sns-sh") || (ForwardEntryPtr->Command == "Snt-sh"));
12480  bool ForwardShuttleFinish = ((ForwardEntryPtr->Command == "Fns-sh") || (ForwardEntryPtr->Command == "Frh-sh"));
12481  bool ReverseShuttleStart = ((ReverseEntryPtr->Command == "Sns-sh") || (ReverseEntryPtr->Command == "Snt-sh"));
12482  bool ReverseShuttleFinish = ((ReverseEntryPtr->Command == "Fns-sh") || (ReverseEntryPtr->Command == "Frh-sh"));
12483 
12484  if(Shuttle && MainTrainDataPtr->ActionVector.back().FormatType != Repeat)
12485  {
12486  SecondPassMessage(GiveMessages, "Error in timetable - shuttle train " + MainHeadCode + " does not have a repeat");
12487  TrainDataVector.clear();
12488  Utilities->CallLogPop(1058);
12489  return false;
12490  }
12491 
12492  if(Shuttle && OtherTrainDataPtr->ActionVector.back().FormatType != Repeat)
12493  {
12494  SecondPassMessage(GiveMessages, "Error in timetable - shuttle train " + OtherHeadCode + " does not have a repeat");
12495  TrainDataVector.clear();
12496  Utilities->CallLogPop(1059);
12497  return false;
12498  }
12499 
12500  if(ForwardEntryPtr->LocationName == "")
12501  {
12502  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + OtherHeadCode +
12503  ". One or other service does not have a location set");
12504  TrainDataVector.clear();
12505  Utilities->CallLogPop(526);
12506  return false;
12507  }
12508 
12509  if(ReverseEntryPtr->LocationName == "")
12510  {
12511  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + OtherHeadCode +
12512  ". One or other service does not have a location set");
12513  TrainDataVector.clear();
12514  Utilities->CallLogPop(527);
12515  return false;
12516  }
12517 
12518  if(ForwardEntryPtr->LocationName != ReverseEntryPtr->LocationName)
12519  {
12520  SecondPassMessage(GiveMessages, "Error in timetable - cross referenced train " + OtherHeadCode +
12521  " is at a different location to the referencing train " + MainHeadCode);
12522  TrainDataVector.clear();
12523  Utilities->CallLogPop(842);
12524  return false;
12525  }
12526 
12527  // ignore shuttle repeat links for first time check
12528  if(!Shuttle)
12529  {
12530  if(ForwardEntryPtr->EventTime != ReverseEntryPtr->EventTime)
12531  {
12532  SecondPassMessage(GiveMessages, "Error in timetable - cross referenced train " + OtherHeadCode +
12533  " has a different event time to the referencing train " + MainHeadCode);
12534  TrainDataVector.clear();
12535  Utilities->CallLogPop(525);
12536  return false;
12537  }
12538  }
12539 
12540  // need to allow for repeat times multiplying up by repeating time for shuttle repeat links
12541  // no need to check from reverse to forward as already checked links consistent, and if include will send message twice
12542  if(ForwardShuttleStart && ReverseShuttleFinish)
12543  // Shuttle must be true if these are true
12544  {
12545  if(!CheckShuttleRepeatTime(0, ForwardEntryPtr->EventTime, ReverseEntryPtr->EventTime, OtherTrainDataPtr->ActionVector.back().RearStartOrRepeatMins))
12546  {
12547  SecondPassMessage(GiveMessages, "Error in timetable - shuttle service " + MainHeadCode +
12548  " first repeat restart time not consistent with finish service " + OtherHeadCode);
12549  TrainDataVector.clear();
12550  Utilities->CallLogPop(1055);
12551  return false;
12552  }
12553  }
12554 
12555  if((ReverseEntryPtr->Command == "Sfs") || (ReverseEntryPtr->Command == "Sns"))
12556  // doesn't matter about ForwardEntryPtr being Sfs/Sns as called for every occurrence of an 'OtherHeadCode' so won't escape
12557  {
12558  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
12559  {
12560  SecondPassMessage(GiveMessages, "Error in timetable - an 'Sfs' or 'Sns' event (" + OtherHeadCode +
12561  ") appears in the same sequence as the corresponding linked event " + MainHeadCode);
12562  TrainDataVector.clear();
12563  Utilities->CallLogPop(528);
12564  return false;
12565  }
12566  }
12567 
12568  if(ReverseEntryPtr->Command == "Fjo")
12569  // doesn't matter about ForwardEntryPtr being Fjo as called for every occurrence of an 'OtherHeadCode' so won't escape
12570  {
12571  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
12572  {
12573  SecondPassMessage(GiveMessages, "Error in timetable - an 'Fjo' event (" + OtherHeadCode +
12574  ") appears in the same sequence as the corresponding linked event " + MainHeadCode);
12575  TrainDataVector.clear();
12576  Utilities->CallLogPop(862);
12577  return false;
12578  }
12579  }
12580 
12581  if(ReverseEntryPtr->Command == "Fns")
12582  // doesn't matter about ForwardEntryPtr being Fns as called for every occurrence of an 'OtherHeadCode' so won't escape
12583  {
12584  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
12585  {
12586  SecondPassMessage(GiveMessages, "Error in timetable - an 'Fns' event (" + OtherHeadCode +
12587  ") appears in the same sequence as the corresponding linked event " + MainHeadCode);
12588  TrainDataVector.clear();
12589  Utilities->CallLogPop(529);
12590  return false;
12591  }
12592  }
12593 
12594  if(ForwardEntryPtr->Command == "Sfs")
12595  {
12596  if((ReverseEntryPtr->Command != "fsp") && (ReverseEntryPtr->Command != "rsp"))
12597  {
12598  SecondPassMessage(GiveMessages,
12599  "Error in timetable - unable to find a corresponding split train event for the train that starts from a split whose headcode is " +
12600  MainHeadCode);
12601  TrainDataVector.clear();
12602  Utilities->CallLogPop(530);
12603  return false;
12604  }
12605  }
12606 
12607  if((ForwardEntryPtr->Command == "fsp") || (ForwardEntryPtr->Command == "rsp"))
12608  {
12609  if(ReverseEntryPtr->Command != "Sfs")
12610  {
12611  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Sfs' event for the train split whose headcode is " +
12612  MainHeadCode);
12613  TrainDataVector.clear();
12614  Utilities->CallLogPop(839);
12615  return false;
12616  }
12617  else
12618  {
12619  if(!(Track->TimetabledLocationNameAllocated(4, ForwardEntryPtr->LocationName)))
12620  {
12621  SecondPassMessage(GiveMessages, "Error in timetable - can't find timetabled location '" + ForwardEntryPtr->LocationName + "' in railway - perhaps there are concourses without platforms?");
12622  TrainDataVector.clear();
12623  Utilities->CallLogPop(849);
12624  return false;
12625  }
12626  if(!(Track->OneNamedLocationElementAtLocation(0, ForwardEntryPtr->LocationName)))
12627  {
12628  SecondPassMessage(GiveMessages, "Error in timetable - can't find any named location elements at '" + ForwardEntryPtr->LocationName + "' - perhaps there are concourses without platforms?");
12629  TrainDataVector.clear();
12630  Utilities->CallLogPop(850);
12631  return false;
12632  }
12633  if(!(Track->OneNamedLocationLongEnoughForSplit(0, ForwardEntryPtr->LocationName)))
12634  {
12635  SecondPassMessage(GiveMessages, "Error in timetable - location too short to split a train at " + ForwardEntryPtr->LocationName);
12636  TrainDataVector.clear();
12637  Utilities->CallLogPop(846);
12638  return false;
12639  }
12640  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
12641  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
12642  if(OtherTrainDataPtr->Description == "")
12643  OtherTrainDataPtr->Description = MainTrainDataPtr->Description;
12644  // NB: May not be set if main train is a service continuation without a description, if so can't do much about it but doesn't affect operation, just the train information display
12645  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
12646  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
12647  }
12648  }
12649 
12650  if(ForwardEntryPtr->Command == "Sns")
12651  {
12652  if(ReverseEntryPtr->Command != "Fns")
12653  {
12654  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Fns' event for the 'Sns' train whose headcode is " +
12655  MainHeadCode + " and is formed from a service with headcode " + OtherHeadCode);
12656  TrainDataVector.clear();
12657  Utilities->CallLogPop(531);
12658  return false;
12659  }
12660  }
12661 
12662  if(ForwardEntryPtr->Command == "Fns")
12663  {
12664  if(ReverseEntryPtr->Command != "Sns")
12665  {
12666  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Sns' event for the train whose headcode is " + MainHeadCode +
12667  " and forms a new service with headcode " + OtherHeadCode);
12668  TrainDataVector.clear();
12669  Utilities->CallLogPop(840);
12670  return false;
12671  }
12672  else
12673  {
12674  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
12675  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
12676  if(OtherTrainDataPtr->Description == "")
12677  OtherTrainDataPtr->Description = MainTrainDataPtr->Description;
12678  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
12679  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
12680  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
12681  }
12682  }
12683  if(ForwardEntryPtr->Command == "jbo")
12684  {
12685  if(ReverseEntryPtr->Command != "Fjo")
12686  {
12687  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Fjo' event for the train whose headcode is " + MainHeadCode +
12688  " and is joined by a train with headcode " + OtherHeadCode);
12689  TrainDataVector.clear();
12690  Utilities->CallLogPop(841);
12691  return false;
12692  }
12693  }
12694 
12695  if(ForwardEntryPtr->Command == "Fjo")
12696  {
12697  if(ReverseEntryPtr->Command != "jbo")
12698  {
12699  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'jbo' event for the train whose headcode is " + MainHeadCode +
12700  " and joins a train with headcode " + OtherHeadCode);
12701  TrainDataVector.clear();
12702  Utilities->CallLogPop(532);
12703  return false;
12704  }
12705  else
12706  {
12707  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
12708  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
12709  if((MainTrainDataPtr->MaxRunningSpeed > 5) && (MainTrainDataPtr->MaxRunningSpeed < OtherTrainDataPtr->MaxRunningSpeed))
12710  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
12711  // added test for > 5 [5 used instead of 0 because of possible floating point errors - though unlikely] above at v1.3.1 because the train will have a zero MaxRunningSpeed if it continues from another service - its max speed is set when it takes over from the other service
12712  // notified of this problem by Ian Walker in his email of 25/03/13. Probably redundant anyway because the max speed is reduced at the changeover if the 'joined by' train's max speed is less.
12713  }
12714  }
12715 
12716  if(ForwardShuttleStart)
12717  // (ForwardEntryPtr->Command == "Sns-sh") || (ForwardEntryPtr->Command == "Snt-sh"))
12718  {
12719  if(!ReverseShuttleFinish)
12720  // (ReverseEntryPtr->Command != "Fns-sh") && (ReverseEntryPtr->Command != "Frh-sh"))
12721  {
12722  SecondPassMessage(GiveMessages, "Error in timetable - incorrect shuttle link to train whose headcode is " + MainHeadCode +
12723  " from train whose headcode is " + OtherHeadCode + ", has to be Fns-sh, Frh-sh");
12724  TrainDataVector.clear();
12725  Utilities->CallLogPop(1056);
12726  return false;
12727  }
12728  }
12729 
12730  if(ReverseShuttleStart)
12731  // (ReverseEntryPtr->Command == "Sns-sh") || (ReverseEntryPtr->Command == "Snt-sh"))
12732  {
12733  if(!ForwardShuttleFinish)
12734  // (ForwardEntryPtr->Command != "Fns-sh") && (ForwardEntryPtr->Command != "Frh-sh"))
12735  {
12736  SecondPassMessage(GiveMessages, "Error in timetable - incorrect shuttle link to train whose headcode is " + OtherHeadCode +
12737  " from train whose headcode is " + MainHeadCode + ", has to be Fns-sh, Frh-sh");
12738  TrainDataVector.clear();
12739  Utilities->CallLogPop(1057);
12740  return false;
12741  }
12742  else
12743  {
12744  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
12745  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
12746 /* don't need LinkedTrainEntryPtr for 'OtherTrain' & don't need data transfer as this is done in the
12747  non-repeating link for Sns-sh & is provided at the outset for Snt-sh
12748 */
12749  }
12750  }
12751 
12752  // check repeat information consistent if present
12753  // note that won't be affected by the non-repeating shuttle links as these are in NonRepeatingShuttleLinkHeadCode
12754  // and those not accessed here
12755 
12756  // still need to check the non-repeating links and that they have no repeats - do that outside this function
12757  bool MainRepeat = false, OtherRepeat = false;
12758  TActionVectorEntry MainRepeatEntry, OtherRepeatEntry;
12759 
12760  if(MainTrainDataPtr->ActionVector.at(MainTrainDataPtr->ActionVector.size() - 1).FormatType == Repeat)
12761  {
12762  MainRepeat = true;
12763  MainRepeatEntry = MainTrainDataPtr->ActionVector.at(MainTrainDataPtr->ActionVector.size() - 1);
12764  }
12765  if(OtherTrainDataPtr->ActionVector.at(OtherTrainDataPtr->ActionVector.size() - 1).FormatType == Repeat)
12766  {
12767  OtherRepeat = true;
12768  OtherRepeatEntry = OtherTrainDataPtr->ActionVector.at(OtherTrainDataPtr->ActionVector.size() - 1);
12769  }
12770  if((MainRepeat && !OtherRepeat) || (!MainRepeat && OtherRepeat))
12771  {
12772  SecondPassMessage(GiveMessages, "Error in timetable - only one repeat is provided for the train whose headcode is " + MainHeadCode +
12773  " and the associated train with headcode " + OtherHeadCode);
12774  TrainDataVector.clear();
12775  Utilities->CallLogPop(844);
12776  return false;
12777  }
12778  if(MainRepeat && OtherRepeat)
12779  {
12780  if((MainRepeatEntry.EventTime != OtherRepeatEntry.EventTime) || (MainRepeatEntry.RearStartOrRepeatMins != OtherRepeatEntry.RearStartOrRepeatMins) ||
12781  (MainRepeatEntry.FrontStartOrRepeatDigits != OtherRepeatEntry.FrontStartOrRepeatDigits) ||
12782  (MainRepeatEntry.NumberOfRepeats != OtherRepeatEntry.NumberOfRepeats))
12783  {
12784  SecondPassMessage(GiveMessages, "Error in timetable - repeat items don't correspond for the train whose headcode is " + MainHeadCode +
12785  " and the associated train with headcode " + OtherHeadCode);
12786  TrainDataVector.clear();
12787  Utilities->CallLogPop(845);
12788  return false;
12789  }
12790  }
12791  Utilities->CallLogPop(863);
12792  return true;
12793 }
12794 
12795 // ---------------------------------------------------------------------------
12796 
12797 void TTrainController::StripSpaces(int Caller, AnsiString &Input)
12798  // strip both leading and trailing spaces at ends of text and spaces before and after all commas and semicolons within the text
12799 {
12800  // strip spaces from extreme ends of input
12801  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",StripSpaces," + AnsiString(Input));
12802  if(Input == "")
12803  {
12804  Utilities->CallLogPop(856);
12805  return;
12806  }
12807  while(Input[1] == ' ')
12808  {
12809  if(Input.Length() > 1)
12810  {
12811  Input = Input.SubString(2, Input.Length() - 1);
12812  }
12813  else
12814  {
12815  Input = "";
12816  Utilities->CallLogPop(857);
12817  return;
12818  }
12819  }
12820  if(Input == "")
12821  {
12822  Utilities->CallLogPop(858);
12823  return;
12824  }
12825  while(Input[Input.Length()] == ' ')
12826  {
12827  if(Input.Length() > 1)
12828  {
12829  Input = Input.SubString(1, Input.Length() - 1);
12830  }
12831  else
12832  {
12833  Input = "";
12834  Utilities->CallLogPop(859);
12835  return;
12836  }
12837  }
12838  // now strip spaces immediately after all commas and semicolons within the text
12839  AnsiString Output = "";
12840  bool DelimiterFound = false;
12841 
12842  for(int x = 1; x < Input.Length() + 1; x++)
12843  {
12844  if(DelimiterFound)
12845  {
12846  if(Input[x] == ' ')
12847  continue;
12848  }
12849  if((Input[x] != ',') && (Input[x] != ';'))
12850  {
12851  DelimiterFound = false;
12852  Output = Output + Input[x];
12853  }
12854  else
12855  {
12856  DelimiterFound = true;
12857  Output = Output + Input[x];
12858  }
12859  }
12860  if(Output == "")
12861  {
12862  Input = "";
12863  Utilities->CallLogPop(860);
12864  return;
12865  }
12866  // now strip spaces immediately before all commas and semicolons within the text
12867  Input = Output;
12868  Output = "";
12869  DelimiterFound = false;
12870  for(int x = Input.Length(); x > 0; x--)
12871  {
12872  if(DelimiterFound)
12873  {
12874  if(Input[x] == ' ')
12875  continue;
12876  }
12877  if((Input[x] != ',') && (Input[x] != ';'))
12878  {
12879  DelimiterFound = false;
12880  Output = AnsiString(Input[x]) + Output;
12881  }
12882  else
12883  {
12884  DelimiterFound = true;
12885  Output = AnsiString(Input[x]) + Output;
12886  }
12887  }
12888  Input = Output;
12889  Utilities->CallLogPop(861);
12890 }
12891 
12892 // ---------------------------------------------------------------------------
12893 
12894 bool TTrainController::IsSNTEntryLocated(int Caller, const TTrainDataEntry &TDEntry, AnsiString &LocationName)
12895  // checks if an Snt or Snt-sh entry followed (somewhere, not necessarily immediately) by a TimeLoc has the same LocationName
12896  // and if so returns true. Also returns true for Snt, not Snt-sh, if at least 1 start element is a location & the entry is either
12897  // a signaller control entry & speed is zero or it is followed immediately by Frh or Fjo (mod at v2.0.0 for empty stock pickup).
12898  // Always return false for entry at a continuation (may be named but not a stop location). Note that no successor validity checks
12899  // are done in this function, they must be done elsewhere.
12900 {
12901  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsSNTEntryLocated," + AnsiString(TDEntry.HeadCode));
12902  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
12903 
12904  LocationName = "";
12905  if((AVEntry0.Command != "Snt") && (AVEntry0.Command != "Snt-sh"))
12906  {
12907  throw Exception("Error, first entry not 'Snt' or 'Snt-sh' in IsSNTEntryLocated");
12908  }
12910  {
12911  Utilities->CallLogPop(852);
12912  return false;
12913  }
12914  AnsiString LocRear = Track->TrackElementAt(507, AVEntry0.RearStartOrRepeatMins).ActiveTrackElementName;
12915  AnsiString LocFront = Track->TrackElementAt(508, AVEntry0.FrontStartOrRepeatDigits).ActiveTrackElementName;
12916 
12917  if(LocRear != "")
12918  LocationName = LocRear;
12919  else
12920  LocationName = LocFront;
12921  if(LocationName == "")
12922  {
12923  Utilities->CallLogPop(1036);
12924  return false;
12925  }
12926  if((AVEntry0.SignallerControl) && (TDEntry.StartSpeed == 0))
12927  {
12928  Utilities->CallLogPop(1773);
12929  return true;
12930  }
12931  else if((AVEntry0.SignallerControl) && (TDEntry.StartSpeed > 0))
12932  {
12933  LocationName = "";
12934  Utilities->CallLogPop(1784);
12935  return false;
12936  }
12937 
12938  // here if not a signaller start entry so must be at least one more entry
12939  const TActionVectorEntry &AVEntry1 = TDEntry.ActionVector.at(1);
12940 
12941  // has to be at least 2 AV entries to pass the > 1 comma test in the preliminary check
12942  if(((AVEntry1.Command == "Frh") || (AVEntry1.Command == "Fjo") || (AVEntry1.Command == "F-nshs") || (AVEntry1.Command == "Fns")) && (AVEntry0.Command == "Snt")) // added Fjo at v2.0.0 for empty stock
12943  { //added F-nshs at v2.5.1 so can stay at a
12944  Utilities->CallLogPop(1037); //location until become a new shuttle service
12945  return true; //added Fns at same time as saw no reason to exclude
12946  }
12947  AnsiString TimeLocLocationName;
12948  bool FoundFlag = false;
12949 
12950  for(unsigned int y = 0; y < TDEntry.ActionVector.size(); y++)
12951  {
12952  const TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(y);
12953  if(AVEntry.FormatType == TimeLoc)
12954  {
12955  FoundFlag = true;
12956  TimeLocLocationName = AVEntry.LocationName;
12957  break;
12958  }
12959  }
12960  if(!FoundFlag)
12961  {
12962  Utilities->CallLogPop(853);
12963  return false;
12964  }
12965  if(TimeLocLocationName == LocationName)
12966  {
12967  Utilities->CallLogPop(854);
12968  return true;
12969  }
12970  Utilities->CallLogPop(855);
12971  return false;
12972 }
12973 
12974 // ---------------------------------------------------------------------------
12975 
12976 bool TTrainController::CheckStartPositionValidity(int Caller, AnsiString RearElementStr, AnsiString FrontElementStr, bool GiveMessages)
12977 {
12978  // checks that the new train start elements are valid - both exist & are connected, and that not
12979  // attempting to start on a diverging leg (i.e. one segment on points & other on element connected to diverging leg)
12980  // & not starting with front on a continuation
12981  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckStartPositionValidity," + RearElementStr + "," + FrontElementStr);
12982  int RearPosition = 0, FrontPosition = 0, RearExitPos = 0;
12983 
12984  RearPosition = Track->GetTrackVectorPositionFromString(5, RearElementStr, GiveMessages);
12985  if(RearPosition < 0)
12986  // error message given in GetTrackVectorPositionFromString
12987  {
12988  Utilities->CallLogPop(759);
12989  return false;
12990  }
12991  FrontPosition = Track->GetTrackVectorPositionFromString(6, FrontElementStr, GiveMessages);
12992  if(FrontPosition < 0)
12993  // error message given in GetTrackVectorPositionFromString
12994  {
12995  Utilities->CallLogPop(760);
12996  return false;
12997  }
12998  TTrackElement RearTrackElement = Track->TrackElementAt(490, RearPosition);
12999  TTrackElement FrontTrackElement = Track->TrackElementAt(491, FrontPosition);
13000  TTrackType RearType = RearTrackElement.TrackType, FrontType = FrontTrackElement.TrackType;
13001 
13002  // check front & rear connected
13003  for(int x = 0; x < 4; x++)
13004  {
13005  if(RearTrackElement.Conn[x] == FrontPosition)
13006  {
13007  RearExitPos = x;
13008  break;
13009  }
13010  if(x == 3)
13011  {
13012  TimetableMessage(GiveMessages, "Front element: " + FrontTrackElement.ElementID + " not linked to rear element: " + RearTrackElement.ElementID);
13013  Utilities->CallLogPop(762);
13014  return false;
13015  }
13016  }
13017  // check not starting with front on a continuation
13018  if(FrontType == Continuation)
13019  {
13020  TimetableMessage(GiveMessages, "Front of train attempting to start on a continuation at: " + FrontElementStr);
13021  Utilities->CallLogPop(937);
13022  return false;
13023  }
13024 
13025  // check not starting on a level crossing
13026  if(Track->IsLCAtHV(43, FrontTrackElement.HLoc, FrontTrackElement.VLoc))
13027  {
13028  TimetableMessage(GiveMessages, "Train attempting to start on a level crossing at: " + FrontElementStr);
13029  Utilities->CallLogPop(1951);
13030  return false;
13031  }
13032  if(Track->IsLCAtHV(44, RearTrackElement.HLoc, RearTrackElement.VLoc))
13033  {
13034  TimetableMessage(GiveMessages, "Train attempting to start on a level crossing at: " + RearElementStr);
13035  Utilities->CallLogPop(1952);
13036  return false;
13037  }
13038 
13039  // check if trying to start on diverging leg of points
13040  if((RearType == Points) && (RearExitPos == 3))
13041  {
13042  TimetableMessage(GiveMessages, "Front of train attempting to start on element connected to diverging points at: " + RearElementStr);
13043  Utilities->CallLogPop(936);
13044  return false;
13045  }
13046 
13047  if((FrontType == Points) && (RearTrackElement.ConnLinkPos[RearExitPos] == 3))
13048  {
13049  TimetableMessage(GiveMessages, "Rear of train attempting to start on element connected to diverging points at: " + FrontElementStr);
13050  Utilities->CallLogPop(1808);
13051  return false;
13052  }
13053  Utilities->CallLogPop(905);
13054  return true;
13055 }
13056 
13057 // ---------------------------------------------------------------------------
13058 
13059 bool TTrainController::CheckStartAllowable(int Caller, int RearPosition, int RearExitPos, AnsiString HeadCode, bool ReportFlag, TActionEventType &EventType)
13060  // Rear & front element validity already checked in CheckStartPositionValidity
13061  // This checks for points in correct orientation, no train at start position and not starting on a locked route
13062 {
13063  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckStartAllowable," + AnsiString(RearPosition) + "," +
13064  AnsiString(RearExitPos));
13065  TTrackElement RearTrackElement = Track->TrackElementAt(517, RearPosition);
13066 
13067  if(RearTrackElement.TrackType == Continuation)
13068  EventType = FailTrainEntry;
13069  else
13070  EventType = FailCreateTrain;
13071  int FrontPosition = RearTrackElement.Conn[RearExitPos];
13072  TTrackElement FrontTrackElement = Track->TrackElementAt(798, FrontPosition);
13073  int FrontEntryPos = RearTrackElement.ConnLinkPos[RearExitPos];
13074  TTrackType RearType = RearTrackElement.TrackType;
13075  TTrackType FrontType = FrontTrackElement.TrackType;
13076  AnsiString RearName, FrontName;
13077 
13078  if(RearTrackElement.ActiveTrackElementName != "")
13079  RearName = RearTrackElement.ActiveTrackElementName;
13080  else
13081  RearName = RearTrackElement.ElementID;
13082  if(FrontTrackElement.ActiveTrackElementName != "")
13083  FrontName = FrontTrackElement.ActiveTrackElementName;
13084  else
13085  FrontName = FrontTrackElement.ElementID;
13086 
13087  TPrefDirElement PrefDirElement; // needed for next function but not used
13088  int LockedVectorNumber; // needed for next function but not used
13089 
13090  if(AllRoutes->IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(12, FrontPosition, FrontEntryPos, PrefDirElement, LockedVectorNumber))
13091  {
13092  if(ReportFlag)
13093  {
13094  if(EventType == FailCreateTrain)
13095  EventType = FailCreateLockedRoute;
13096  else
13097  EventType = FailEnterLockedRoute;
13098  LogActionError(47, HeadCode, "", EventType, FrontName);
13099  }
13100  Utilities->CallLogPop(940);
13101  return false;
13102  }
13103 
13104  if(AllRoutes->IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(13, RearPosition, RearExitPos, PrefDirElement, LockedVectorNumber))
13105  {
13106  if(ReportFlag)
13107  {
13108  if(EventType == FailCreateTrain)
13109  EventType = FailCreateLockedRoute;
13110  else
13111  EventType = FailEnterLockedRoute;
13112  LogActionError(48, HeadCode, "", EventType, RearName);
13113  }
13114  Utilities->CallLogPop(1809);
13115  return false;
13116  }
13117 
13118  if((RearType != Bridge) && (RearTrackElement.TrainIDOnElement > -1))
13119  {
13120  if(ReportFlag)
13121  {
13122  LogActionError(27, HeadCode, "", EventType, RearName);
13123  }
13124  Utilities->CallLogPop(1810);
13125  return false;
13126  }
13127 
13128  if((FrontType != Bridge) && (FrontTrackElement.TrainIDOnElement > -1))
13129  {
13130  if(ReportFlag)
13131  {
13132  if(EventType == FailCreateTrain)
13133  LogActionError(28, HeadCode, "", EventType, FrontName);
13134  else
13135  LogActionError(43, HeadCode, "", EventType, RearName);
13136  }
13137  Utilities->CallLogPop(941);
13138  return false;
13139  }
13140  if(RearType == Bridge)
13141  {
13142  if((RearExitPos > 1) && (RearTrackElement.TrainIDOnBridgeTrackPos23 > -1))
13143  {
13144  if(ReportFlag)
13145  {
13146  LogActionError(29, HeadCode, "", EventType, RearName);
13147  }
13148  Utilities->CallLogPop(942);
13149  return false;
13150  }
13151  if((RearExitPos < 2) && (RearTrackElement.TrainIDOnBridgeTrackPos01 > -1))
13152  {
13153  if(ReportFlag)
13154  {
13155  LogActionError(30, HeadCode, "", EventType, RearName);
13156  }
13157  Utilities->CallLogPop(943);
13158  return false;
13159  }
13160  }
13161  if(FrontType == Bridge)
13162  {
13163  if((FrontEntryPos > 1) && (FrontTrackElement.TrainIDOnBridgeTrackPos23 > -1))
13164  {
13165  if(ReportFlag)
13166  {
13167  if(EventType == FailCreateTrain)
13168  LogActionError(31, HeadCode, "", EventType, FrontName);
13169  else
13170  LogActionError(44, HeadCode, "", EventType, RearName);
13171  }
13172  Utilities->CallLogPop(944);
13173  return false;
13174  }
13175  if((FrontEntryPos < 2) && (FrontTrackElement.TrainIDOnBridgeTrackPos01 > -1))
13176  {
13177  if(ReportFlag)
13178  {
13179  if(EventType == FailCreateTrain)
13180  LogActionError(45, HeadCode, "", EventType, FrontName);
13181  else
13182  LogActionError(46, HeadCode, "", EventType, RearName);
13183  }
13184  Utilities->CallLogPop(945);
13185  return false;
13186  }
13187  }
13188 
13189  EventType = FailCreatePoints;
13190  if(RearType == Points)
13191  {
13192  if(RearTrackElement.Attribute == 1)
13193  {
13194  if(ReportFlag)
13195  {
13196  LogActionError(33, HeadCode, "", FailCreatePoints, RearName);
13197  }
13198  Utilities->CallLogPop(933);
13199  return false;
13200  }
13201  }
13202  if(FrontType == Points)
13203  {
13204  if(FrontTrackElement.Attribute == 1)
13205  {
13206  if(ReportFlag)
13207  {
13208  LogActionError(34, HeadCode, "", FailCreatePoints, FrontName);
13209  }
13210  Utilities->CallLogPop(934);
13211  return false;
13212  }
13213  }
13214  Utilities->CallLogPop(939);
13215  return true;
13216 }
13217 
13218 // ---------------------------------------------------------------------------
13219 
13220 AnsiString TTrainController::GetRepeatHeadCode(int Caller, AnsiString BaseHeadCode, int RepeatNumber, int IncDigits)
13221 {
13222  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetRepeatHeadCode," + BaseHeadCode + "," + AnsiString(RepeatNumber) +
13223  "," + AnsiString(IncDigits));
13224  if(!Last2CharactersBothDigits(1, BaseHeadCode) && (IncDigits > 0))
13225  {
13226  throw Exception("Error, last 2 characters not both digits and IncDigits > 0 in GetRepeatHeadCode");
13227  }
13228  if(!Last2CharactersBothDigits(2, BaseHeadCode))
13229  {
13230  Utilities->CallLogPop(1893);
13231  return BaseHeadCode;
13232  }
13233  int BaseDigits = BaseHeadCode.SubString(3, 2).ToInt();
13234  int NextRepeatDigits = BaseDigits + (IncDigits * RepeatNumber);
13235 
13236  while(NextRepeatDigits >= 100)
13237  {
13238  NextRepeatDigits -= 100; // rolls over after 99
13239  }
13240  AnsiString NextRepeatDigitsStr = AnsiString(NextRepeatDigits);
13241 
13242  if(NextRepeatDigitsStr.Length() < 2)
13243  NextRepeatDigitsStr = AnsiString('0') + NextRepeatDigitsStr;
13244  AnsiString NextRepeatHeadCode = BaseHeadCode.SubString(1, 2) + NextRepeatDigitsStr;
13245 
13246  Utilities->CallLogPop(1365);
13247  return NextRepeatHeadCode;
13248 }
13249 
13250 // ---------------------------------------------------------------------------
13251 
13252 TDateTime TTrainController::GetRepeatTime(int Caller, TDateTime BasicTime, int RepeatNumber, int IncMinutes)
13253 {
13254  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetRepeatTime," + AnsiString(double(BasicTime)) + "," +
13255  AnsiString(RepeatNumber) + "," + AnsiString(IncMinutes));
13256  TDateTime NextRepeatTime = BasicTime + TDateTime(((double)(RepeatNumber * IncMinutes)) / 1440.0); // 1440 = no. of minutes in 24h
13257 
13258  Utilities->CallLogPop(1366);
13259  return NextRepeatTime;
13260 }
13261 
13262 // ---------------------------------------------------------------------------
13263 
13264 bool TTrainController::CheckShuttleRepeatTime(int Caller, TDateTime ForwardEventTime, TDateTime ReverseEventTime, int RepeatMinutes)
13265  // For success the ForwardEventTime + repeat time should == ReverseEventTime (allow 10secs either way since converting to doubles)
13266 {
13267  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckShuttleRepeatTime," + AnsiString(double(ForwardEventTime)) + "," +
13268  AnsiString(double(ReverseEventTime)) + "," + AnsiString(RepeatMinutes));
13269  int ForwardSecs = int(double(ForwardEventTime) * 86400);
13270  int ReverseSecs = int(double(ReverseEventTime) * 86400);
13271  int RepeatSecs = RepeatMinutes * 60;
13272 
13273  if((ForwardSecs > (ReverseSecs - RepeatSecs + 10)) || (ForwardSecs < (ReverseSecs - RepeatSecs - 10)))
13274  {
13275  Utilities->CallLogPop(1367);
13276  return false;
13277  }
13278  else
13279  {
13280  Utilities->CallLogPop(1368);
13281  return true;
13282  }
13283 }
13284 
13285 // ---------------------------------------------------------------------------
13286 
13287 bool TTrainController::CheckNonRepeatingShuttleLinksAndSetData(int Caller, AnsiString MainHeadCode, AnsiString NonRepeatingHeadCode, bool GiveMessages)
13288  // check for proper non-repeating link cross references and that they have no repeats & that times are consistent
13289 {
13290  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckNonRepeatingShuttleLinksAndSetData," + MainHeadCode + "," +
13291  NonRepeatingHeadCode);
13292  int ForwardCount = 0;
13293  int ReverseCount = 0;
13294  unsigned int ForwardTDVectorNumber, ReverseTDVectorNumber;
13295  TActionVectorEntry *ReverseEntryPtr = 0, *ForwardEntryPtr = 0;
13296  // Forward corresponds to Main, Reverse to Other
13297  TTrainDataEntry *MainTrainDataPtr = 0;
13298  TTrainDataEntry *OtherTrainDataPtr = 0;
13299 
13300  // forward check
13301  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13302  {
13303  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13304  if(TDEntry.HeadCode == MainHeadCode)
13305  {
13306  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13307  {
13308  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13309  if(AVEntry.NonRepeatingShuttleLinkHeadCode == NonRepeatingHeadCode)
13310  {
13311  MainTrainDataPtr = &TrainDataVector.at(x);
13312  ForwardEntryPtr = &AVEntry;
13313  ForwardCount++;
13314  ForwardTDVectorNumber = x;
13315  }
13316  }
13317  }
13318  }
13319  if(ForwardCount == 0)
13320  // this is an exception because the headcodes are selected in the same order as the forward check
13321  {
13322  throw Exception("Error, ForwardCount == 0 in CheckNonRepeatingShuttleLinksAndSetData after called with found values");
13323  }
13324  if(ForwardCount > 1)
13325  {
13326  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + NonRepeatingHeadCode + " from a train whose headcode is " +
13327  MainHeadCode);
13328  TrainDataVector.clear();
13329  Utilities->CallLogPop(1061);
13330  return false;
13331  }
13332 
13333  // reverse check
13334  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13335  {
13336  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13337  if(TDEntry.HeadCode == NonRepeatingHeadCode)
13338  {
13339  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13340  {
13341  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13342  if(AVEntry.NonRepeatingShuttleLinkHeadCode == MainHeadCode)
13343  {
13344  OtherTrainDataPtr = &TrainDataVector.at(x);
13345  ReverseCount++;
13346  ReverseEntryPtr = &AVEntry;
13347  ReverseTDVectorNumber = x;
13348  }
13349  }
13350  }
13351  }
13352 
13353  if(ReverseCount == 0)
13354  {
13355  SecondPassMessage(GiveMessages, "Error in timetable - cross reference missing in either " + MainHeadCode + " or " + NonRepeatingHeadCode);
13356  TrainDataVector.clear();
13357  Utilities->CallLogPop(1062);
13358  return false;
13359  }
13360  if(ReverseCount > 1)
13361  {
13362  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + MainHeadCode + " from a train whose headcode is " +
13363  NonRepeatingHeadCode);
13364  TrainDataVector.clear();
13365  Utilities->CallLogPop(1063);
13366  return false;
13367  }
13368 
13369  if(((ForwardEntryPtr->Command == "F-nshs") || (ForwardEntryPtr->Command == "Sns-fsh")) && (MainTrainDataPtr->ActionVector.back().FormatType == Repeat))
13370  {
13371  SecondPassMessage(GiveMessages, "Error in timetable - shuttle connecting train " + MainHeadCode + " shouldn't have a repeat");
13372  TrainDataVector.clear();
13373  Utilities->CallLogPop(1064);
13374  return false;
13375  }
13376 
13377  if((ForwardEntryPtr->Command != "F-nshs") && (ForwardEntryPtr->Command != "Sns-fsh") && (MainTrainDataPtr->ActionVector.back().FormatType != Repeat))
13378  {
13379  SecondPassMessage(GiveMessages, "Error in timetable - shuttle train " + MainHeadCode + " does not have a repeat item");
13380  TrainDataVector.clear();
13381  Utilities->CallLogPop(1065);
13382  return false;
13383  }
13384 
13385  if(ForwardEntryPtr->LocationName == "")
13386  {
13387  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + NonRepeatingHeadCode +
13388  ". One or other service does not have a location set");
13389  TrainDataVector.clear();
13390  Utilities->CallLogPop(1066);
13391  return false;
13392  }
13393 
13394  if(ReverseEntryPtr->LocationName == "")
13395  {
13396  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + NonRepeatingHeadCode +
13397  ". One or other service does not have a location set");
13398  TrainDataVector.clear();
13399  Utilities->CallLogPop(1067);
13400  return false;
13401  }
13402 
13403  if(ForwardEntryPtr->LocationName != ReverseEntryPtr->LocationName)
13404  {
13405  SecondPassMessage(GiveMessages, "Error in timetable - cross referenced train " + NonRepeatingHeadCode +
13406  " is at a different location to the referencing train " + MainHeadCode);
13407  TrainDataVector.clear();
13408  Utilities->CallLogPop(1068);
13409  return false;
13410  }
13411 
13412  if(ForwardEntryPtr->Command == "F-nshs")
13413  // i.e. the non repeating link into the shuttle service
13414  {
13415  if(ForwardEntryPtr->EventTime != ReverseEntryPtr->EventTime)
13416  {
13417  SecondPassMessage(GiveMessages, "Error in timetable - shuttle in-link service " + MainHeadCode +
13418  " finish time not consistent with start time of shuttle service " + NonRepeatingHeadCode);
13419  TrainDataVector.clear();
13420  Utilities->CallLogPop(1069);
13421  return false;
13422  }
13423  }
13424 
13425  if(ForwardEntryPtr->Command == "Fns-sh")
13426  // i.e. the non repeating link out from the shuttle service
13427  {
13428  if(!CheckNonRepeatingShuttleLinkTime(0, ForwardEntryPtr->EventTime, ReverseEntryPtr->EventTime,
13429  MainTrainDataPtr->ActionVector.back().RearStartOrRepeatMins, MainTrainDataPtr->ActionVector.back().NumberOfRepeats))
13430  {
13431  SecondPassMessage(GiveMessages, "Error in timetable - service " + NonRepeatingHeadCode + ", which links out from shuttle service " + MainHeadCode +
13432  ", has the wrong start time. It should correspond to the finish time of the last shuttle.");
13433  TrainDataVector.clear();
13434  Utilities->CallLogPop(1070);
13435  return false;
13436  }
13437  }
13438 
13439  if((ForwardEntryPtr->Command == "F-nshs") || (ForwardEntryPtr->Command == "Sns-fsh"))
13440  // i.e. a non repeating link to or from the shuttle service
13441  {
13442  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
13443  {
13444  SecondPassMessage(GiveMessages, "Error in timetable - the non repeating link service " + NonRepeatingHeadCode +
13445  " appears in the same sequence as the corresponding shuttle service " + MainHeadCode);
13446  TrainDataVector.clear();
13447  Utilities->CallLogPop(1071);
13448  return false;
13449  }
13450  }
13451 /* it's allowed to have a different description
13452  if((ForwardEntryPtr->Command == "F-nshs") || (ForwardEntryPtr->Command == "Sns-fsh"))//i.e. a non repeating link to or from the shuttle service
13453  {
13454  if((MainTrainDataPtr->Description != "") && (OtherTrainDataPtr->Description != "") && (MainTrainDataPtr->Description != OtherTrainDataPtr->Description))
13455  {
13456  SecondPassMessage(GiveMessages, "Error in timetable - the non repeating link service " + NonRepeatingHeadCode + " has a different description to the corresponding shuttle service " + MainHeadCode);
13457  TrainDataVector.clear();
13458  Utilities->CallLogPop(1072);
13459  return false;
13460  }
13461  }
13462 */
13463  if(ForwardEntryPtr->Command == "Sns-sh")
13464  {
13465  if(ReverseEntryPtr->Command != "F-nshs")
13466  {
13467  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'F-nshs' event for the 'Sns-sh' train whose headcode is " +
13468  MainHeadCode + " and is a new shuttle service formed from the service with headcode " + NonRepeatingHeadCode);
13469  TrainDataVector.clear();
13470  Utilities->CallLogPop(1073);
13471  return false;
13472  }
13473  }
13474 
13475  if(ForwardEntryPtr->Command == "F-nshs")
13476  {
13477  if(ReverseEntryPtr->Command != "Sns-sh")
13478  {
13479  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Sns-sh' event for the 'F-nshs' train whose headcode is " +
13480  MainHeadCode + " and forms a new shuttle service with headcode " + NonRepeatingHeadCode);
13481  TrainDataVector.clear();
13482  Utilities->CallLogPop(1074);
13483  return false;
13484  }
13485  else
13486  {
13487  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
13488  ReverseEntryPtr->NonRepeatingShuttleLinkEntryPtr = MainTrainDataPtr;
13489  if(OtherTrainDataPtr->Description == "")
13490  OtherTrainDataPtr->Description = MainTrainDataPtr->Description;
13491  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
13492  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
13493  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
13494  }
13495  }
13496 
13497  if(ForwardEntryPtr->Command == "Sns-fsh")
13498  {
13499  if(ReverseEntryPtr->Command != "Fns-sh")
13500  {
13501  SecondPassMessage(GiveMessages,
13502  "Error in timetable - unable to find a corresponding 'Fns-sh' event for the 'Sns-fsh' non-shuttle service whose headcode is " + MainHeadCode +
13503  " formed from a shuttle service with headcode " + NonRepeatingHeadCode);
13504  TrainDataVector.clear();
13505  Utilities->CallLogPop(1075);
13506  return false;
13507  }
13508  }
13509 
13510  if(ForwardEntryPtr->Command == "Fns-sh")
13511  {
13512  if(ReverseEntryPtr->Command != "Sns-fsh")
13513  {
13514  SecondPassMessage(GiveMessages,
13515  "Error in timetable - unable to find a corresponding 'Sns-fsh' event for the 'Fns-sh' shuttle service whose headcode is " + MainHeadCode +
13516  " and forms a new non-shuttle service with headcode " + NonRepeatingHeadCode);
13517  TrainDataVector.clear();
13518  Utilities->CallLogPop(1076);
13519  return false;
13520  }
13521  else
13522  {
13523  ForwardEntryPtr->NonRepeatingShuttleLinkEntryPtr = OtherTrainDataPtr;
13524  // links to the non-repeating non-shuttle linked service
13525  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
13526  // needed for creating formatted timetable
13527  if(OtherTrainDataPtr->Description == "")
13528  OtherTrainDataPtr->Description = MainTrainDataPtr->Description;
13529  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
13530  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
13531  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
13532  }
13533  }
13534 
13535  Utilities->CallLogPop(1077);
13536  return true;
13537 }
13538 
13539 // ---------------------------------------------------------------------------
13540 
13541 bool TTrainController::CheckNonRepeatingShuttleLinkTime(int Caller, TDateTime ForwardEventTime, TDateTime ReverseEventTime, int RepeatMinutes, int RepeatNumber)
13542  // Forward train is the finish shuttle entry 'Fns-sh'.
13543  // The Reverse (new non-repeating service) time must == Forward time + (RepeatMins * RepeatNumber) but allow 10 secs either side
13544 {
13545  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckNonRepeatingShuttleLinkTime," + AnsiString(double(ForwardEventTime))
13546  + "," + AnsiString(double(ReverseEventTime)) + "," + AnsiString(RepeatMinutes) + "," + AnsiString(RepeatNumber));
13547  int ForwardSecs = int(double(ForwardEventTime) * 86400);
13548  int ReverseSecs = int(double(ReverseEventTime) * 86400);
13549  int RepeatSecs = RepeatMinutes * RepeatNumber * 60;
13550 
13551  if((ReverseSecs > (ForwardSecs + RepeatSecs + 10)) || (ReverseSecs < (ForwardSecs + RepeatSecs - 10)))
13552  {
13553  Utilities->CallLogPop(1369);
13554  return false;
13555  }
13556  else
13557  {
13558  Utilities->CallLogPop(1370);
13559  return true;
13560  }
13561 }
13562 
13563 // ---------------------------------------------------------------------------
13564 
13565 bool TTrainController::CheckShuttleServiceIntegrity(int Caller, TTrainDataEntry *TDEntryPtr, bool GiveMessages)
13566  // check that each shuttle start ends either in Fns or Fxx-sh (though a single service can't end in Fxx-sh), and that
13567  // when the Fxx-sh is reached it references the original start and not another shuttle - not allowed to link two shuttles,
13568  // don't ever need to and as designed would skip repeats.
13569 
13570  // enter with TDEntry a shuttle start - Snt-sh or Sns-sh
13571 {
13572  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckShuttleServiceIntegrity," + AnsiString(TDEntryPtr->HeadCode));
13573  if(TDEntryPtr->ActionVector.back().FormatType != Repeat)
13574  {
13575  throw Exception("Error - last entry in " + TDEntryPtr->HeadCode + " service is not a repeat - should have already found this error");
13576  }
13577  TTrainDataEntry *ShuttleStartAddress = TDEntryPtr;
13578  AnsiString OriginalHeadCode = TDEntryPtr->HeadCode;
13579  AnsiString LastActionCommand = (TDEntryPtr->ActionVector.end() - 2)->Command;
13580 
13581  if((LastActionCommand != "Fns") && (LastActionCommand != "Fns-sh") && (LastActionCommand != "Frh-sh"))
13582  {
13583  SecondPassMessage(GiveMessages, "Error in timetable - last event in shuttle service " + TDEntryPtr->HeadCode + " is not 'Fns', 'Fns-sh' or 'Frh-sh'");
13584  TrainDataVector.clear();
13585  Utilities->CallLogPop(1091);
13586  return false;
13587  }
13588  while(LastActionCommand == "Fns")
13589  {
13590  TDEntryPtr = (TDEntryPtr->ActionVector.end() - 2)->LinkedTrainEntryPtr;
13591  LastActionCommand = (TDEntryPtr->ActionVector.end() - 2)->Command;
13592  if((LastActionCommand != "Fns") && (LastActionCommand != "Fns-sh") && (LastActionCommand != "Frh-sh"))
13593  {
13594  SecondPassMessage(GiveMessages,
13595  "Error in timetable - last event in a continuation shuttle service (i.e links back to a shuttle) whose headcode is " + TDEntryPtr->HeadCode +
13596  " is not 'Fns', 'Fns-sh' or 'Frh-sh'");
13597  TrainDataVector.clear();
13598  Utilities->CallLogPop(1092);
13599  return false;
13600  }
13601  }
13602  // exit the 'while' with LastActionCommand FSH-XX
13603  if((TDEntryPtr->ActionVector.end() - 2)->LinkedTrainEntryPtr != ShuttleStartAddress)
13604  {
13605  SecondPassMessage(GiveMessages, "Error in timetable - the event that ends service " + TDEntryPtr->HeadCode +
13606  " is a shuttle finish, but it doesn't link back to the start of the original shuttle starting service " + OriginalHeadCode +
13607  ". The linking of two or more shuttles is not permitted.");
13608  TrainDataVector.clear();
13609  Utilities->CallLogPop(1093);
13610  return false;
13611  }
13612  Utilities->CallLogPop(1094);
13613  return true;
13614 }
13615 
13616 // ---------------------------------------------------------------------------
13617 
13618 void TTrainController::TimetableMessage(bool GiveMessages, AnsiString Message)
13619 {
13620  if(!GiveMessages)
13621  return;
13622 
13623  // if(ServiceReference == "") ShowMessage(Message);
13624  if(!CheckHeadCodeValidity(12, false, ServiceReference))
13625  ShowMessage(Message);
13626  // changed from above at v2.3.0 as a meaningless value for 'Timetable invalid - unable to find a valid start time on its own line' (uses last entry text)
13627  // false means don't give messages within the function
13628  else
13629  ShowMessage("Service " + ServiceReference + ": " + Message);
13630 }
13631 
13632 // ---------------------------------------------------------------------------
13633 
13634 void TTrainController::SecondPassMessage(bool GiveMessages, AnsiString Message)
13635 {
13636  if(!GiveMessages)
13637  return;
13638 
13639  ShowMessage(Message);
13640 }
13641 
13642 // $$$$$$$$$$$$$$$$$$$$$$$ End of Timetable Functions $$$$$$$$$$$$$$$$$$$$$$$
13643 // ---------------------------------------------------------------------------
13644 
13645 void TTrainController::LogActionError(int Caller, AnsiString HeadCode, AnsiString OtherHeadCode, TActionEventType ActionEventType, AnsiString LocationID)
13646  // FailTrainEntry: 06:00:10 HELD: 2F43 can't enter railway, train obstructing entry position 57-N5
13647  // FailCreateTrain: 06:00:10 HELD: 2F43 can't be created, train obstructing start position 57-N5
13648  // FailCreateLockedRoute: 06:00:10 HELD: 2F43 can't be created on a locked route - start position 57-N5
13649  // FailEnterLockedRoute: 06:00:10 HELD: 2F43 can't enter on a locked route - start position 57-N5
13650  // FailCreatePoints: 06:00:10 HELD: 2F43 can't be created, points set to diverge at start position 57-N5
13651  // FailUnexpectedExitRailway: 06:00:10 ERROR: 2F43 left railway unexpectedly at position 57-N5
13652  // FailIncorrectExit: 06:00:10 ERROR: 2F43 left railway at an incorrect exit at position 57-N5
13653  // FailSPAD: 06:00:10 ERROR: 2F43 PASSED SIGNAL AT DANGER at position 57-N5
13654  // FailLockedRoute: 06:00:10 ERROR: SPAD Risk! Signals reset ahead of train, at position 57-N5
13655  // FailLocTooShort: 06:00:10 ERROR: 2F43 failed to split - location too short at Essex Road
13656  // FailSplitDueToOtherTrain: 06:00:10 HELD: 2F43 unable to split - another train is obstructing at Essex Road
13657  // FailCrashed: 06:00:10: ERROR: 2F43 CRASHED INTO 3F43 at position 46-N7
13658  // FailDerailed: 06:00:10: ERROR: 2F43 DERAILED at position 46-N7
13659  // FailUnexpectedBuffers: 06:00:10: ERROR: 2F43 stopped at buffers unexpectedly at position 46-N7
13660  // FailMissedArrival: 06:00:10: ERROR: 2F43 failed to stop at Essex Road;
13661  // FailMissedSplit: 06:00:10: ERROR: 2F43 failed to split at Essex Road
13662  // FailMissedJBO: 06:00:10: ERROR: 2F43 failed to be joined by join other train at Essex Road
13663  // FailMissedJoinOther: 06:00:10: ERROR: 2F43 failed to join other train at Essex Road
13664  // FailMissedTerminate: 06:00:10: ERROR: 2F43 failed to terminate at Essex Road
13665  // FailMissedNewService: 06:00:10: ERROR: 2F43 failed to form new service at Essex Road
13666  // FailMissedExitRailway: 06:00:10: ERROR: 2F43 failed to exit railway
13667  // FailMissedChangeDirection: 06:00:10: ERROR: 2F43 failed to change direction at Essex Road
13668  // FailMissedPass: 06:00:10: ERROR: 2F43 failed to pass Essex Road
13669  // FailBuffersPreventingStart: 06:00:10: ERROR: 2F43 facing buffers and unable to start at Essex Road
13670  // FailBufferCrash: 06:00:10: ERROR: 2F43 CRASHED INTO BUFFERS at 46-N7
13671  // FailLevelCrossingCrash: 06:00:10: ERROR: 2F43 CRASHED INTO ROAD TRAFFIC AT A LEVEL CROSSING at 46-N7
13672  // RouteForceCancelled: 06:00:10: ERROR: 2F43 forced a route cancellation by occupying it incorrectly at 46-N7
13673  // WaitingForJBO: 06:00:10: WARNING: 2F43 waiting to join 3F43 at Essex Road
13674  // WaitingForFJO: 06:00:10: WARNING: 2F43 waiting to be joined by 3F43 at Essex Road
13675 {
13676  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LogActionError," + HeadCode + "," + OtherHeadCode + "," +
13677  AnsiString(ActionEventType) + "," + LocationID);
13678  AnsiString BaseLog = "", Prefix = "", ErrorLog = "", WarningStr = "";
13679 
13680  Prefix = " ERROR: ";
13681  if(ActionEventType == FailTrainEntry)
13682  {
13683  Prefix = " HELD: ";
13684  ErrorLog = " can't enter railway, train obstructing entry position ";
13685  WarningStr = " can't enter railway, train obstructing entry position ";
13686  Display->WarningLog(1, HeadCode + WarningStr + LocationID);
13687  }
13688  else if(ActionEventType == FailCreateTrain)
13689  {
13690  Prefix = " HELD: ";
13691  ErrorLog = " can't be created, train obstructing ";
13692  WarningStr = " can't be created, train obstructing ";
13693  Display->WarningLog(2, HeadCode + WarningStr + LocationID);
13694  }
13695  else if(ActionEventType == FailCreateLockedRoute)
13696  {
13697  Prefix = " HELD: ";
13698  ErrorLog = " can't be created on a locked route at ";
13699  WarningStr = " can't be created on a locked route at ";
13700  Display->WarningLog(4, HeadCode + WarningStr + LocationID);
13701  }
13702  else if(ActionEventType == FailEnterLockedRoute)
13703  {
13704  Prefix = " HELD: ";
13705  ErrorLog = " can't enter on a locked route at ";
13706  WarningStr = " can't enter on a locked route at ";
13707  Display->WarningLog(5, HeadCode + WarningStr + LocationID);
13708  }
13709  else if(ActionEventType == FailCreatePoints)
13710  {
13711  Prefix = " HELD: ";
13712  ErrorLog = " can't be created, diverging points at ";
13713  WarningStr = " can't be created, diverging points at ";
13714  Display->WarningLog(3, HeadCode + WarningStr + LocationID);
13715  }
13716  else if(ActionEventType == FailUnexpectedExitRailway)
13717  {
13718  ErrorLog = " left railway unexpectedly at ";
13719  UnexpectedExits++;
13720  }
13721  else if(ActionEventType == FailIncorrectExit)
13722  {
13723  ErrorLog = " left railway at an incorrect exit at ";
13724  IncorrectExits++;
13725  }
13726  else if(ActionEventType == FailLocTooShort)
13727  {
13728  ErrorLog = " failed to split - location too short at ";
13729  WarningStr = " failed to split, location too short at ";
13730  Display->WarningLog(6, HeadCode + WarningStr + LocationID);
13731  }
13732  else if(ActionEventType == FailSplitDueToOtherTrain)
13733  {
13734  Prefix = " HELD: ";
13735  ErrorLog = " unable to split - other train obstructing at ";
13736  WarningStr = " unable to split - other train obstructing at ";
13737  Display->WarningLog(7, HeadCode + WarningStr + LocationID);
13738  }
13739  else if(ActionEventType == FailUnexpectedBuffers)
13740  ErrorLog = " stopped at buffers unexpectedly at position ";
13741  else if(ActionEventType == FailMissedArrival)
13742  {
13743  ErrorLog = " failed to stop at ";
13744  MissedStops++;
13745  }
13746  else if(ActionEventType == FailMissedSplit)
13747  {
13748  ErrorLog = " failed to split at ";
13750  }
13751  else if(ActionEventType == FailMissedJBO)
13752  {
13753  ErrorLog = " failed to be joined by other train at ";
13755  }
13756  else if(ActionEventType == FailMissedJoinOther)
13757  {
13758  ErrorLog = " failed to join other train at ";
13760  }
13761  else if(ActionEventType == FailMissedTerminate)
13762  {
13763  ErrorLog = " failed to terminate at ";
13765  }
13766  else if(ActionEventType == FailMissedNewService)
13767  {
13768  ErrorLog = " failed to form new service at ";
13770  }
13771  else if(ActionEventType == FailMissedExitRailway)
13772  {
13773  ErrorLog = " failed to exit railway ";
13775  }
13776  else if(ActionEventType == FailMissedChangeDirection)
13777  {
13778  ErrorLog = " failed to change direction at ";
13780  }
13781  else if(ActionEventType == FailMissedPass)
13782  {
13783  ErrorLog = " failed to pass ";
13785  }
13786  else if(ActionEventType == FailBuffersPreventingStart)
13787  ErrorLog = " facing buffers and unable to start at ";
13788  else if(ActionEventType == FailDerailed)
13789  {
13790  ErrorLog = " DERAILED at position ";
13791  Prefix = " DERAILMENT: ";
13792  Derailments++;
13793  }
13794  else if(ActionEventType == FailBufferCrash)
13795  {
13796  ErrorLog = " CRASHED INTO BUFFERS at ";
13797  Prefix = " CRASH: ";
13798  CrashedTrains++;
13799  }
13800  else if(ActionEventType == FailLevelCrossingCrash)
13801  {
13802  ErrorLog = " CRASHED INTO ROAD TRAFFIC AT A LEVEL CROSSING at ";
13803  Prefix = " CRASH: ";
13804  CrashedTrains++;
13805  }
13806  else if(ActionEventType == FailCrashed)
13807  {
13808  ErrorLog = " CRASHED INTO " + OtherHeadCode + " at position ";
13809  Prefix = " CRASH: ";
13810  CrashedTrains++;
13811  CrashedTrains++;
13812  }
13813  else if(ActionEventType == FailSPAD)
13814  {
13815  ErrorLog = " PASSED SIGNAL AT DANGER at position ";
13816  Prefix = " SPAD: ";
13817  SPADEvents++;
13818  }
13819  else if(ActionEventType == FailLockedRoute)
13820  {
13821  ErrorLog = "Signals reset ahead of train, route cancelled at position ";
13822  Prefix = " SPAD RISK: ";
13823  SPADRisks++;
13824  }
13825  else if(ActionEventType == RouteForceCancelled)
13826  {
13827  ErrorLog = " forced a route cancellation by occupying it incorrectly at ";
13828  }
13829  else if(ActionEventType == WaitingForJBO)
13830  {
13831  Prefix = " WARNING: ";
13832  ErrorLog = " waiting to join " + OtherHeadCode + " at ";
13833  WarningStr = " waiting to join " + OtherHeadCode + " at ";
13834  Display->WarningLog(8, HeadCode + WarningStr + LocationID);
13835  }
13836  else if(ActionEventType == WaitingForFJO)
13837  {
13838  Prefix = " WARNING: ";
13839  ErrorLog = " waiting to be joined by " + OtherHeadCode + " at ";
13840  WarningStr = " waiting to be joined by " + OtherHeadCode + " at ";
13841  Display->WarningLog(9, HeadCode + WarningStr + LocationID);
13842  }
13843  TDateTime ActualTime = TrainController->TTClockTime;
13844 
13845  BaseLog = Utilities->Format96HHMMSS(ActualTime) + Prefix + HeadCode;
13846  Display->PerformanceLog(4, BaseLog + ErrorLog + LocationID);
13847  Utilities->CallLogPop(1371);
13848 }
13849 
13850 // ---------------------------------------------------------------------------
13851 
13853 {
13854 /*
13855  TrainDataEntry
13856  AnsiString HeadCode, Description;//null on creation
13857  int StartSpeed, MaxRunningSpeed;//both kph
13858  int RepeatNumber;
13859  TActionVector ActionVector;
13860  TTrainOperatingDataVector TrainOperatingDataVector;//no of repeats + 1
13861  TTrainDataEntry() {StartSpeed=0; MaxRunningSpeed=0; RepeatNumber=0;}
13862 
13863  ActionVectorEntry
13864  TTimetableEntryType FormatType;
13865  TDateTime EventTime, ArrivalTime, DepartureTime;//zeroed on creation so change to -1 as a marker for 'not set'
13866  AnsiString LocationName, Command, OtherHeadCode;//null on creation
13867  TActionVectorEntry *OtherHeadCodeStartingEntryPtr;
13868  int RearStartOrRepeatMins, FrontStartOrRepeatDigits;
13869  int RepeatNumber;
13870 
13871  TrainOperatingData
13872  int Mass, MaxBrakeRate, PowerAtRail;//kg;m/s/s;W
13873  int TrainID;
13874  TRunningEntry RunningEntry;
13875  TDateTime StartTime;
13876  AnsiString HeadCode;
13877 */
13878  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveTrainDataVectorToFile");
13879  std::ofstream OutFile("TrainData.csv");
13880 
13881  if(OutFile == 0)
13882  {
13883  ShowMessage("Output file TrainData.csv failed to open");
13884  Utilities->CallLogPop(1372);
13885  return;
13886  }
13887  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13888  {
13889  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13890  OutFile << "HeadCode" << ',' << "Description" << ',' << "StartSpeed" << ',' << "MaxRunningSpeed" << ',' << "NumberOfTrains" << '\n' << '\n';
13891 
13892  OutFile << TDEntry.HeadCode.c_str() << ',' << TDEntry.Description.c_str()
13893  << ',' << TDEntry.StartSpeed << ',' << TDEntry.MaxRunningSpeed << ',' << TDEntry.NumberOfTrains << '\n' << '\n';
13894 
13895  OutFile << ',' << "FormatType" << ',' << "EventTime" << ',' << "ArrivalTime" << ',' << "DepartureTime" << ',' << "LocationName" << ',' << "Command" <<
13896  ',' << "OtherHeadCode" << ',' << "LinkedTrainEntryPtr" << ',' << "RearStartOrRepeatMins" << ',' << "FrontStartOrRepeatDigits" << ',' <<
13897  "RepeatNumber" << '\n' << '\n';
13898  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13899  {
13900  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13901  AnsiString TimetableEntryTypeStr;
13902  // NoFormat, TimeLoc, TimeTimeLoc, TimeCmd, StartNew, TimeCmdHeadCode, FinRemHere, FNSNonRepeatToShuttle, SNTShuttle, SNSShuttle, SNSNonRepeatFromShuttle, FSHNewService, Repeat
13903  switch(AVEntry.FormatType)
13904  {
13905  case 0:
13906  {
13907  TimetableEntryTypeStr = "NoFormat";
13908  break;
13909  }
13910 
13911  case 1:
13912  {
13913  TimetableEntryTypeStr = "TimeLoc";
13914  break;
13915  }
13916 
13917  case 2:
13918  {
13919  TimetableEntryTypeStr = "TimeTimeLoc";
13920  break;
13921  }
13922 
13923  case 3:
13924  {
13925  TimetableEntryTypeStr = "TimeCmd";
13926  break;
13927  }
13928 
13929  case 4:
13930  {
13931  TimetableEntryTypeStr = "StartNew";
13932  break;
13933  }
13934 
13935  case 5:
13936  {
13937  TimetableEntryTypeStr = "TimeCmdHeadCode";
13938  break;
13939  }
13940 
13941  case 6:
13942  {
13943  TimetableEntryTypeStr = "FinRemHere";
13944  break;
13945  }
13946 
13947  case 7:
13948  {
13949  TimetableEntryTypeStr = "FNSShuttle";
13950  break;
13951  }
13952 
13953  case 8:
13954  {
13955  TimetableEntryTypeStr = "SNTShuttle";
13956  break;
13957  }
13958 
13959  case 9:
13960  {
13961  TimetableEntryTypeStr = "SNSShuttle";
13962  break;
13963  }
13964 
13965  case 10:
13966  {
13967  TimetableEntryTypeStr = "SNSNonRepeatFromShuttle";
13968  break;
13969  }
13970 
13971  case 11:
13972  {
13973  TimetableEntryTypeStr = "FSHNewService";
13974  break;
13975  }
13976 
13977  case 12:
13978  {
13979  TimetableEntryTypeStr = "Repeat";
13980  break;
13981  }
13982 
13983  default:
13984  {
13985  TimetableEntryTypeStr = "Default";
13986  break;
13987  }}
13988  OutFile << ',' << TimetableEntryTypeStr.c_str() << ',' << Utilities->Format96HHMM(AVEntry.EventTime).c_str() << ',' << Utilities->Format96HHMM
13989  (AVEntry.ArrivalTime).c_str() << ',' << Utilities->Format96HHMM(AVEntry.DepartureTime).c_str() << ',' << AVEntry.LocationName.c_str()
13990  << ',' << AVEntry.Command.c_str() << ',' << AVEntry.OtherHeadCode.c_str()
13991  << ',' << AVEntry.LinkedTrainEntryPtr << ',' << AVEntry.RearStartOrRepeatMins << ',' << AVEntry.FrontStartOrRepeatDigits << ',' <<
13992  AVEntry.NumberOfRepeats << '\n';
13993  }
13994  OutFile << '\n';
13995  OutFile << ',' << ',' << "Mass" << ',' << "MaxBrakeRate" << ',' << "PowerAtRail" << ',' << "TrainID" << ',' << "RunningEntry" << '\n' << '\n';
13996  for(unsigned int y = 0; y < TrainDataVector.at(x).TrainOperatingDataVector.size(); y++)
13997  {
13998  TTrainOperatingData TOD = TrainDataVector.at(x).TrainOperatingDataVector.at(y);
13999  AnsiString RunningEntryStr;
14000  // NotStarted, Running, Exited
14001  switch(TOD.RunningEntry)
14002  {
14003  case 0:
14004  {
14005  RunningEntryStr = "NotStarted";
14006  break;
14007  }
14008 
14009  case 1:
14010  {
14011  RunningEntryStr = "Running";
14012  break;
14013  }
14014 
14015  case 2:
14016  {
14017  RunningEntryStr = "Exited";
14018  break;
14019  }}
14020  OutFile << ',' << ',' << TOD.TrainID << ',' << RunningEntryStr.c_str() << ',' << '\n';
14021  }
14022  OutFile << '\n';
14023  }
14024  OutFile.close();
14025  Utilities->CallLogPop(1373);
14026 }
14027 
14028 // ---------------------------------------------------------------------------
14029 
14030 void TTrainController::StopTTClockMessage(int Caller, AnsiString Message)
14031  // ShowMessage stops everything so this function used where a message is needed when may be in Operating mode.
14032  // The timetable Restart and BaseTimes are reset so the timetable clock stops & restarts when 'OK' button pressed
14033 {
14034  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",StopTTClockMessage," + Message);
14035  StopTTClockFlag = true; // so TTClock stopped during MasterClockTimer function
14037  ShowMessage(Message);
14038  BaseTime = TDateTime::CurrentDateTime();
14039  StopTTClockFlag = false;
14040  Utilities->CallLogPop(1374);
14041 }
14042 
14043 // ---------------------------------------------------------------------------
14044 
14045 void TTrainController::SaveSessionTrains(int Caller, std::ofstream &SessionFile)
14046  // save *TrainDataEntryPtr & *ActionVectorEntryPtr as integer offsets
14047  // from the start of the relevant vectors. Can't save the pointer values
14048  // as these will be different each time the vectors are created
14049 {
14050  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveSessionTrains");
14051  Utilities->SaveFileInt(SessionFile, TrainVector.size());
14052  for(unsigned int x = 0; x < TrainVector.size(); x++)
14053  {
14054  TrainVectorAt(55, x).SaveOneSessionTrain(0, SessionFile);
14055  }
14056  Utilities->CallLogPop(1375);
14057 }
14058 
14059 // ---------------------------------------------------------------------------
14060 
14061 void TTrainController::LoadSessionTrains(int Caller, std::ifstream &SessionFile)
14062 {
14063  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadSessionTrains");
14064  int NumberOfTrains = Utilities->LoadFileInt(SessionFile);
14065  TTrain *NewTrain = new TTrain(1, 0, 0, "", 0, 1, 0, 0, 0, (TTrainMode)0, 0, 0, 0, 0, 0); // have to have >0 for mass, else have divide
14066 
14067  // by zero error in calculating AValue, use 1
14068  for(int x = 0; x < NumberOfTrains; x++)
14069  {
14070  *NewTrain = TTrain(2, 0, 0, "", 0, 1, 0, 0, 0, (TTrainMode)0, 0, 0, 0, 0, 0); // have to have >0 for mass, else have divide
14071  // by zero error in calculating AValue, use 1
14072  NewTrain->LoadOneSessionTrain(0, SessionFile);
14073  if((NewTrain->EntrySpeed < 1) && (NewTrain->PowerAtRail < 1))
14074  // added at v2.4.0. have to include as that value not stored in session file
14075  {
14076  NewTrain->StoppedWithoutPower = true;
14077  }
14078  TrainVector.push_back(*NewTrain);
14079  LastTrainLoaded = x;
14080  }
14081  delete NewTrain;
14082  Utilities->CallLogPop(1376);
14083 }
14084 
14085 // ---------------------------------------------------------------------------
14086 
14087 bool TTrainController::CheckSessionTrains(int Caller, std::ifstream &InFile)
14088 {
14089  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSessionTrains");
14090  int NumberOfTrains;
14091 
14092  if(!Utilities->CheckAndReadFileInt(InFile, 0, 10000, NumberOfTrains))
14093  {
14094  Utilities->CallLogPop(1377);
14095  return false;
14096  }
14097  for(int x = 0; x < NumberOfTrains; x++)
14098  {
14099  if(!(TTrain::CheckOneSessionTrain(InFile)))
14100  {
14101  Utilities->CallLogPop(1378);
14102  return false;
14103  }
14104  }
14105  Utilities->CallLogPop(1379);
14106  return true;
14107 }
14108 
14109 // ---------------------------------------------------------------------------
14110 
14111 void TTrainController::SaveSessionLockedRoutes(int Caller, std::ofstream &SessionFile)
14112 {
14113  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveSessionLockedRoutes");
14114  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.size());
14115  for(unsigned int x = 0; x < AllRoutes->LockedRouteVector.size(); x++)
14116  {
14117  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).RouteNumber);
14118  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).TruncateTrackVectorPosition);
14119  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).LastTrackVectorPosition);
14120  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).LastXLinkPos);
14121  Utilities->SaveFileDouble(SessionFile, double(AllRoutes->LockedRouteVector.at(x).LockStartTime));
14122  }
14123  Utilities->CallLogPop(1380);
14124 }
14125 
14126 // ---------------------------------------------------------------------------
14127 
14128 void TTrainController::LoadSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
14129 {
14130  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadSessionLockedRoutes");
14131  TAllRoutes::TLockedRouteClass LockedRouteObject;
14132  int LockedRouteVectorSize = Utilities->LoadFileInt(SessionFile);
14133 
14134  for(int x = 0; x < LockedRouteVectorSize; x++)
14135  {
14136  LockedRouteObject.RouteNumber = Utilities->LoadFileInt(SessionFile);
14137  LockedRouteObject.TruncateTrackVectorPosition = Utilities->LoadFileInt(SessionFile);
14138  LockedRouteObject.LastTrackVectorPosition = Utilities->LoadFileInt(SessionFile);
14139  LockedRouteObject.LastXLinkPos = Utilities->LoadFileInt(SessionFile);
14140  double LockStartTimeDouble = Utilities->LoadFileDouble(SessionFile);
14141  LockedRouteObject.LockStartTime = TDateTime(LockStartTimeDouble);
14142  AllRoutes->LockedRouteVector.push_back(LockedRouteObject);
14143  }
14144  Utilities->CallLogPop(1381);
14145 }
14146 
14147 // ---------------------------------------------------------------------------
14148 
14149 bool TTrainController::CheckSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
14150 {
14151  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSessionLockedRoutes");
14152  int LockedRouteVectorSize;
14153 
14154  if(!Utilities->CheckAndReadFileInt(SessionFile, 0, 10000, LockedRouteVectorSize))
14155  {
14156  Utilities->CallLogPop(1382);
14157  return false;
14158  }
14159  for(int x = 0; x < LockedRouteVectorSize; x++)
14160  {
14161  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
14162  {
14163  Utilities->CallLogPop(1383);
14164  return false;
14165  }
14166  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
14167  {
14168  Utilities->CallLogPop(1384);
14169  return false;
14170  }
14171  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
14172  {
14173  Utilities->CallLogPop(1385);
14174  return false;
14175  }
14176  if(!Utilities->CheckFileInt(SessionFile, 0, 3))
14177  {
14178  Utilities->CallLogPop(1386);
14179  return false;
14180  }
14181  if(!Utilities->CheckFileDouble(SessionFile))
14182  {
14183  Utilities->CallLogPop(1387);
14184  return false;
14185  }
14186  }
14187  Utilities->CallLogPop(1388);
14188  return true;
14189 }
14190 
14191 // ---------------------------------------------------------------------------
14192 
14193 void TTrainController::SaveSessionContinuationAutoSigEntries(int Caller, std::ofstream &SessionFile)
14194 {
14195  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveSessionContinuationAutoSigEntries");
14196  Utilities->SaveFileInt(SessionFile, ContinuationAutoSigVector.size());
14197  for(unsigned int x = 0; x < ContinuationAutoSigVector.size(); x++)
14198  {
14199  Utilities->SaveFileInt(SessionFile, ContinuationAutoSigVector.at(x).RouteNumber);
14200  Utilities->SaveFileInt(SessionFile, ContinuationAutoSigVector.at(x).AccessNumber);
14201  Utilities->SaveFileDouble(SessionFile, ContinuationAutoSigVector.at(x).FirstDelay);
14202  Utilities->SaveFileDouble(SessionFile, ContinuationAutoSigVector.at(x).SecondDelay);
14203  Utilities->SaveFileDouble(SessionFile, ContinuationAutoSigVector.at(x).ThirdDelay);
14204  Utilities->SaveFileDouble(SessionFile, double(ContinuationAutoSigVector.at(x).PassoutTime));
14205  }
14206  Utilities->CallLogPop(1389);
14207 }
14208 
14209 // ---------------------------------------------------------------------------
14210 
14211 void TTrainController::LoadSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
14212 {
14213  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadSessionContinuationAutoSigEntries");
14214  TContinuationAutoSigEntry ContinuationAutoSigObject;
14215  int ContinuationAutoSigVectorSize = Utilities->LoadFileInt(SessionFile);
14216 
14217  for(int x = 0; x < ContinuationAutoSigVectorSize; x++)
14218  {
14219  ContinuationAutoSigObject.RouteNumber = Utilities->LoadFileInt(SessionFile);
14220  ContinuationAutoSigObject.AccessNumber = Utilities->LoadFileInt(SessionFile);
14221  ContinuationAutoSigObject.FirstDelay = Utilities->LoadFileDouble(SessionFile);
14222  ContinuationAutoSigObject.SecondDelay = Utilities->LoadFileDouble(SessionFile);
14223  ContinuationAutoSigObject.ThirdDelay = Utilities->LoadFileDouble(SessionFile);
14224  double PassoutTimeDouble = Utilities->LoadFileDouble(SessionFile);
14225  ContinuationAutoSigObject.PassoutTime = TDateTime(PassoutTimeDouble);
14226  ContinuationAutoSigVector.push_back(ContinuationAutoSigObject);
14227  }
14228  Utilities->CallLogPop(1390);
14229 }
14230 
14231 // ---------------------------------------------------------------------------
14232 
14233 bool TTrainController::CheckSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
14234 {
14235  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSessionContinuationAutoSigEntries");
14236  int ContinuationAutoSigVectorSize;
14237 
14238  if(!Utilities->CheckAndReadFileInt(SessionFile, 0, 10000, ContinuationAutoSigVectorSize))
14239  {
14240  Utilities->CallLogPop(1391);
14241  return false;
14242  }
14243  for(int x = 0; x < ContinuationAutoSigVectorSize; x++)
14244  {
14245  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
14246  {
14247  Utilities->CallLogPop(1392);
14248  return false;
14249  }
14250  if(!Utilities->CheckFileInt(SessionFile, 0, 3))
14251  {
14252  Utilities->CallLogPop(1393);
14253  return false;
14254  }
14255  if(!Utilities->CheckFileDouble(SessionFile))
14256  {
14257  Utilities->CallLogPop(1405);
14258  return false;
14259  }
14260  if(!Utilities->CheckFileDouble(SessionFile))
14261  {
14262  Utilities->CallLogPop(1406);
14263  return false;
14264  }
14265  if(!Utilities->CheckFileDouble(SessionFile))
14266  {
14267  Utilities->CallLogPop(1407);
14268  return false;
14269  }
14270  if(!Utilities->CheckFileDouble(SessionFile))
14271  {
14272  Utilities->CallLogPop(1394);
14273  return false;
14274  }
14275  }
14276  Utilities->CallLogPop(1395);
14277  return true;
14278 }
14279 
14280 // ---------------------------------------------------------------------------
14281 
14282 /*
14283  class TContinuationTrainExpectationEntry //for expected trains at continuation entries
14284  {
14285  public:
14286  AnsiString Description; ///< service description
14287  AnsiString HeadCode; ///< service headcode
14288  int RepeatNumber; ///< service RepeatNumber
14289  int IncrementalMinutes; ///< Repeat separation in minutes
14290  int IncrementalDigits; ///< Repeat headcode separation
14291  int VectorPosition; ///< TrackVectorPosition for the continuation element
14292  TTrainDataEntry *TrainDataEntryPtr; ///< points to the service entry in the timetable's TrainDataVector
14293  };
14294 
14295 
14296  typedef std::multimap<TDateTime,TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMap;
14297  typedef pair<TDateTime, TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMapPair;
14298 */
14299 
14301  // build this into timetable load so session loading can use it too
14302  // being a multimap it automatically sorts in ascending EventTime order
14303 {
14304  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",BuildContinuationTrainExpectationMultiMap");
14306  // need to clear as this called twice when load a session
14307  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14308  {
14309  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
14310  const TActionVectorEntry &AVFirstEntry = TDEntry.ActionVector.at(0);
14311  const TActionVectorEntry &AVLastEntry = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
14312 
14313  if(AVFirstEntry.Command == "Snt")
14314  // new train (no need to include Snt-sh since they can't start at a continuation)
14315  {
14318  {
14320  CTEEntry.VectorPosition = AVFirstEntry.RearStartOrRepeatMins;
14321  // retains this value for all repeats
14322  CTEEntry.RepeatNumber = 0; // for first entry
14323  CTEEntry.TrainDataEntryPtr = &TDEntry;
14324  // retains this value for all repeats
14325  CTEEntry.HeadCode = TDEntry.HeadCode;
14326  CTEEntry.Description = TDEntry.Description;
14327  CTEEntry.IncrementalMinutes = 0;
14328  CTEEntry.IncrementalDigits = 0;
14329  if(AVLastEntry.FormatType == Repeat)
14330  {
14331  CTEEntry.IncrementalMinutes = AVLastEntry.RearStartOrRepeatMins;
14332  // retains this value or 0 for all repeats
14333  CTEEntry.IncrementalDigits = AVLastEntry.FrontStartOrRepeatDigits;
14334  // retains this value or 0 for all repeats
14335  }
14336  CTEMMP.first = AVFirstEntry.EventTime;
14337  CTEMMP.second = CTEEntry;
14338  ContinuationTrainExpectationMultiMap.insert(CTEMMP);
14339  // base entry
14340  if(TDEntry.NumberOfTrains > 1)
14341  {
14342  if(AVLastEntry.FormatType != Repeat)
14343  {
14344  throw Exception("Error, Last ActionVectorEntry not a repeat in BuildContinuationTrainExpectationMultiMap");
14345  }
14346  for(int y = 1; y < TDEntry.NumberOfTrains; y++)
14347  {
14348  CTEEntry.RepeatNumber = y;
14349  CTEEntry.HeadCode = GetRepeatHeadCode(23, TDEntry.HeadCode, y, AVLastEntry.FrontStartOrRepeatDigits);
14350  // CTEEntry.VectorPosition stays same
14351  CTEMMP.first = GetRepeatTime(3, AVFirstEntry.EventTime, y, AVLastEntry.RearStartOrRepeatMins);
14352  CTEMMP.second = CTEEntry;
14353  ContinuationTrainExpectationMultiMap.insert(CTEMMP);
14354  }
14355  }
14356  }
14357  }
14358  }
14359  Utilities->CallLogPop(1396);
14360 }
14361 
14362 // ---------------------------------------------------------------------------
14363 
14365 {
14366  // called when WarningFlashCount == 0 or when press zoomout button
14367  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainsInZoomOutMode");
14368  if(!Display->ZoomOutFlag)
14369  {
14370  Utilities->CallLogPop(1156);
14371  return;
14372  }
14373  for(unsigned int x = 0; x < TrainVector.size(); x++)
14374  {
14375  // plot blanks & track for all train, even if to be overplotted, since when flashing need to overplot all anyway
14376  // if OldPlotElement[x] == -1 then ignore (not plotted)
14378  TrainVectorAt(57, x).PlotTrainInZoomOutMode(0, Flash);
14379  }
14380  Display->Update();
14381  // need to keep this since Update() not called for PlotSmallOutput as too slow
14382  Utilities->CallLogPop(742);
14383 }
14384 
14385 // ---------------------------------------------------------------------------
14386 
14387 TTrain &TTrainController::TrainVectorAt(int Caller, int VecPos)
14388 {
14389  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainVectorAt," + AnsiString(VecPos));
14390  if((VecPos < 0) || (VecPos >= (int)TrainVector.size()))
14391  {
14392  throw Exception("Out of Range Error, vector size: " + AnsiString(TrainVector.size()) + ", VecPos: " + AnsiString(VecPos) + " in TrainVectorAt");
14393  }
14394  Utilities->CallLogPop(740);
14395  return TrainVector.at(VecPos);
14396 }
14397 
14398 // ---------------------------------------------------------------------------
14399 
14400 void TTrainController::CreateFormattedTimetable(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir)
14401 {
14402  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CreateFormattedTimetable");
14403  AnsiString RetStr = "", PartStr = "";
14404 
14405 
14406 /*
14407  Have description & mass etc for train at top - header, then array of actions
14408 
14409  class TActionVectorEntry
14410  {
14411  public:
14412  AnsiString LocationName, Command, OtherHeadCode, NonRepeatingShuttleLinkHeadCode;
14414  bool SignallerControl;
14416  bool Warning;
14418  int NumberOfRepeats;
14420  int RearStartOrRepeatMins, FrontStartOrRepeatDigits;
14422  TDateTime EventTime, ArrivalTime, DepartureTime;
14424  TExitList ExitList;
14426  TTimetableFormatType FormatType;
14428  TTimetableLocationType LocationType;
14430  TTimetableSequenceType SequenceType;
14432  TTimetableShuttleLinkType ShuttleLinkType;
14434  TTrainDataEntry *LinkedTrainEntryPtr;
14436  TTrainDataEntry *NonRepeatingShuttleLinkEntryPtr;
14438 
14439  typedef std::vector<TActionVectorEntry> TActionVector;//contains all actions for a single train
14440 
14441  enum TRunningEntry {NotStarted, Running, Exited};//contains status info for each train
14442 
14443  class TTrainOperatingData
14444  {
14445  public:
14446  int TrainID;
14447  TActionEventType EventReported;
14448  TRunningEntry RunningEntry;
14449 
14450  //inline function
14451  TTrainOperatingData() {TrainID = -1; EventReported= NoEvent; RunningEntry=NotStarted;}//ID -1 = marker for not running
14452  };
14453 
14454  typedef std::vector<TTrainOperatingData> TTrainOperatingDataVector;
14455 
14456  class TTrainDataEntry
14457  {
14458  public:
14459  AnsiString HeadCode, ServiceReference, Description;
14461  double MaxBrakeRate;
14463  double MaxRunningSpeed;
14465  double PowerAtRail;
14467  int Mass;
14469  int NumberOfTrains;
14471  int SignallerSpeed;
14473  int StartSpeed;
14475  TActionVector ActionVector;
14477  TTrainOperatingDataVector TrainOperatingDataVector;
14479 
14480  //inline function
14481  TTrainDataEntry() {StartSpeed=0; MaxRunningSpeed=0; NumberOfTrains=0;}
14482  };
14483 
14484  typedef std::vector<TTrainDataEntry> TTrainDataVector;//object is a member of TTrainController & contains the whole timetable
14485 
14486  //formatted timetable types
14487  class TOneTrainFormattedEntry
14488  {
14489  AnsiString Action;//includes location if relevanr
14490  AnsiString Time;
14491  };
14492 
14493  typedef std::vector<TOneTrainFormattedEntry> TOneFormattedTrainVector;
14494 
14495  class TOneCompleteFormattedTrain//headcode + list of actions
14496  {
14497  public:
14498  AnsiString HeadCode;
14499  TOneFormattedTrainVector OneFormattedTrainVector;
14500  };
14501 
14502  typedef std::vector<TOneCompleteFormattedTrain> TOneCompleteFormattedTrainVector;//list af all repeats
14503 
14504  class TTrainFormattedInformation//contains all information for a single TT entry (including repeats)
14505  {
14506  public:
14507  AnsiString Header;//description, mass, power, brake rate etc
14508  int NumberOfTrains;// number of repeats + 1
14509  TOneCompleteFormattedTrainVector OneCompleteFormattedTrainVector;//list af all repeats
14510  };
14511 
14512 
14513  typedef std::vector<TTrainFormattedInformation> TAllFormattedTrains;//all timetable in formatted form
14514  //end of formatted timetable types
14515 
14516 */
14517 
14518  AnsiString TTFileName = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
14519 
14520  // format "16/06/2009 20:55:17"
14521  // avoid characters in filename:= / \ : * ? " < > |
14522  TTFileName = CurDir + "\\Formatted timetables\\Timetable " + TTFileName + "; " + RailwayTitle + "; " + TimetableTitle + ".csv";
14523 
14524  AnsiString ShortTTName = "";
14525 
14526  for(int x = TTFileName.Length(); x > 0; x--)
14527  {
14528  if(TTFileName[x] == '\\')
14529  {
14530  ShortTTName = TTFileName.SubString(x + 1, TTFileName.Length() - x - 4);
14531  break;
14532  }
14533  }
14534 
14535  ShowMessage("Creates two timetables named " + ShortTTName +
14536  " in the 'Formatted timetables' folder, one in service order in '.csv' format, and one in chronological order in '.txt' format");
14537 
14538  Screen->Cursor = TCursor(-11); // Hourglass
14539 
14540  AnsiString FormatNoDPStr = "#######0";
14541  AnsiString TableTitle = "", TimetableTimeStr = "", MassStr = "", PowerStr = "", BrakeStr = "", MaxSpeedStr = "", FirstHeadCode = "", Header = "";
14542 
14544  TableTitle = "Railway: " + RailwayTitle + "; Timetable: " + TimetableTitle + "; Start time: " + TimetableTimeStr;
14545  TAllFormattedTrains *AllTTTrains = new TAllFormattedTrains;
14546 
14547  // all timetable in formatted form
14548  //create the AllTTTrains vector
14549  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14550  {
14551  MassStr = "", PowerStr = "", BrakeStr = "", MaxSpeedStr = "";
14552  const TTrainDataEntry &TrainDataEntry = TrainDataVector.at(x);
14553  if(TrainDataEntry.Mass > 0)
14554  MassStr = "; Mass " + AnsiString::FormatFloat(FormatNoDPStr, ((double)TrainDataEntry.Mass) / 1000) + "Te; ";
14555  if(TrainDataEntry.PowerAtRail > 0)
14556  PowerStr = "Power " + AnsiString::FormatFloat(FormatNoDPStr, TrainDataEntry.PowerAtRail / 1000 / 0.8) + "kW; ";
14557  if(TrainDataEntry.MaxBrakeRate > 0)
14558  BrakeStr = "Brake force " + AnsiString::FormatFloat(FormatNoDPStr, (TrainDataEntry.MaxBrakeRate * TrainDataEntry.Mass / 9810)) + "Te; ";
14559  if(TrainDataEntry.MaxRunningSpeed > 0)
14560  MaxSpeedStr = "Maximum speed " + AnsiString::FormatFloat(FormatNoDPStr, TrainDataEntry.MaxRunningSpeed) + " km/h";
14561  FirstHeadCode = TrainDataEntry.HeadCode;
14562  int IncDigits = 0, IncMinutes = 0;
14563  const TActionVector &ActionVector = TrainDataEntry.ActionVector;
14564  if(!ActionVector.empty())
14565  {
14566  if(ActionVector.at(ActionVector.size() - 1).FormatType == Repeat)
14567  {
14568  IncDigits = ActionVector.at(ActionVector.size() - 1).FrontStartOrRepeatDigits;
14569  IncMinutes = ActionVector.at(ActionVector.size() - 1).RearStartOrRepeatMins;
14570  }
14571  }
14572 
14573  TTrainFormattedInformation OneTTLine;
14574  // contains all information for a single TT entry (including repeats)
14575  for(int y = 0; y < TrainDataEntry.NumberOfTrains; y++)
14576  {
14577  OneTTLine.Header = "";
14578  if((TrainDataEntry.Description != "") && (MassStr != ""))
14579  OneTTLine.Header = TrainDataEntry.Description + MassStr + PowerStr + BrakeStr + MaxSpeedStr;
14580  else if(TrainDataEntry.Description != "")
14581  OneTTLine.Header = TrainDataEntry.Description;
14582  OneTTLine.NumberOfTrains = TrainDataEntry.NumberOfTrains;
14583  TOneCompleteFormattedTrain OneTTTrain; // headcode + list of actions
14584  for(unsigned int z = 0; z < ActionVector.size(); z++)
14585  {
14586  TOneTrainFormattedEntry OneTTEntry;
14587  OneTTTrain.HeadCode = GetRepeatHeadCode(24, FirstHeadCode, y, IncDigits);
14588  TActionVectorEntry ActionVectorEntry = ActionVector.at(z);
14589  AnsiString PartStr = "", TimeStr = "";
14590 /*
14591  enum TTimetableFormatType {NoFormat, TimeLoc, TimeTimeLoc, TimeCmd, StartNew, TimeCmdHeadCode, FinRemHere,
14592  FNSNonRepeatToShuttle, SNTShuttle, SNSShuttle, SNSNonRepeatFromShuttle, FSHNewService, Repeat, PassTime,
14593  ExitRailway};
14594  enum TTimetableSequenceType {NoSequence, Start, Finish, Intermediate, SequTypeForRepeatEntry};
14595  enum TTimetableLocationType {NoLocation, AtLocation, EnRoute, LocTypeForRepeatEntry};
14596  enum TTimetableShuttleLinkType {NoShuttleLink, NotAShuttleLink, ShuttleLink, ShuttleLinkTypeForRepeatEntry};
14597 */
14598  if(ActionVectorEntry.SequenceType == Start)
14599  {
14600  if(ActionVectorEntry.FormatType == StartNew)
14601  {
14602  if(ActionVectorEntry.LocationName != "")
14603  {
14604  if(Track->TrackElementAt(742, ActionVectorEntry.RearStartOrRepeatMins).TrackType == Continuation)
14605  {
14606  PartStr = "Enters at " + ActionVectorEntry.LocationName;
14607  }
14608  else
14609  {
14610  PartStr = "Created at " + ActionVectorEntry.LocationName;
14611  }
14612  }
14613  else // may be a named continuation or other element, and if so report that
14614  {
14615  AnsiString LocName = Track->TrackElementAt(739, ActionVectorEntry.RearStartOrRepeatMins).ActiveTrackElementName;
14616  if(Track->TrackElementAt(740, ActionVectorEntry.RearStartOrRepeatMins).TrackType == Continuation)
14617  {
14618  if(LocName != "")
14619  {
14620  PartStr = "Enters at " + LocName;
14621  }
14622  else // use rear position if it's a continuation
14623  {
14624  PartStr = "Enters at " + Track->TrackElementAt(737, ActionVectorEntry.RearStartOrRepeatMins).ElementID;
14625  }
14626  }
14627  else // not a continuation
14628  {
14629  if(LocName != "")
14630  // if not a continuation then LocName should be same as ActionVectorEntry.LocationName
14631  // but include anyway
14632  {
14633  PartStr = "Created at " + LocName;
14634  }
14635  else // use rear position again
14636  {
14637  PartStr = "Created at " + Track->TrackElementAt(741, ActionVectorEntry.RearStartOrRepeatMins).ElementID;
14638  }
14639  }
14640  }
14641  TimeStr = Utilities->Format96HHMM(GetRepeatTime(20, ActionVectorEntry.EventTime, y, IncMinutes));
14642  }
14643  else if(ActionVectorEntry.FormatType == SNTShuttle)
14644  {
14645  if(y == 0) // first train
14646  {
14647  PartStr = "Enters at " + ActionVectorEntry.LocationName;
14648  TimeStr = Utilities->Format96HHMM(GetRepeatTime(21, ActionVectorEntry.EventTime, y, IncMinutes));
14649  }
14650  else
14651  {
14652  PartStr = "Repeat shuttle service at " + ActionVectorEntry.LocationName + " from ";
14653  TimeStr = GetRepeatHeadCode(45, ActionVectorEntry.OtherHeadCode, y - 1, IncDigits) + " at " +
14654  Utilities->Format96HHMM(GetRepeatTime(26, ActionVectorEntry.EventTime, y, IncMinutes));
14655  } // y-1 for headcode above since it is the last repeat value that the train is from
14656  }
14657  else if(ActionVectorEntry.Command == "Sfs")
14658  {
14659  PartStr = "New service at " + ActionVectorEntry.LocationName + " split from";
14660  TimeStr = GetRepeatHeadCode(33, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
14661  Utilities->Format96HHMM(GetRepeatTime(24, ActionVectorEntry.EventTime, y, IncMinutes));
14662  }
14663  else if(ActionVectorEntry.Command == "Sns")
14664  {
14665  PartStr = "New service at " + ActionVectorEntry.LocationName + " from";
14666  TimeStr = GetRepeatHeadCode(34, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
14667  Utilities->Format96HHMM(GetRepeatTime(25, ActionVectorEntry.EventTime, y, IncMinutes));
14668  }
14669  else if(ActionVectorEntry.FormatType == SNSShuttle)
14670  {
14671  if(y == 0) // first entry from shuttle
14672  {
14673  PartStr = "New service at " + ActionVectorEntry.LocationName + " from";
14674  TimeStr = ActionVectorEntry.NonRepeatingShuttleLinkHeadCode + " at " +
14675  Utilities->Format96HHMM(GetRepeatTime(27, ActionVectorEntry.EventTime, y, IncMinutes));
14676  }
14677  else
14678  {
14679  PartStr = "Repeat shuttle service at " + ActionVectorEntry.LocationName + " from ";
14680  TimeStr = GetRepeatHeadCode(35, ActionVectorEntry.OtherHeadCode, y - 1, IncDigits) + " at " +
14681  Utilities->Format96HHMM(GetRepeatTime(22, ActionVectorEntry.EventTime, y, IncMinutes));
14682  } // y-1 for headcode above since it is the last repeat value that the train is from
14683  }
14684  else if(ActionVectorEntry.FormatType == SNSNonRepeatFromShuttle)
14685  {
14686  PartStr = "New service at " + ActionVectorEntry.LocationName + " from";
14687  // need repeat for the non-repeating headcode as it's the last train of the repeating shuttle
14688  TTrainDataEntry *TDE = ActionVectorEntry.LinkedTrainEntryPtr;
14689  AnsiString FirstHeadCode = TDE->HeadCode;
14690  int LastRepeatNumber = TDE->NumberOfTrains - 1;
14691  // a shuttle has to have at least 1 repeat
14692  int IncrementalDigits = TDE->ActionVector.at(TDE->ActionVector.size() - 1).FrontStartOrRepeatDigits;
14693  TimeStr = GetRepeatHeadCode(36, FirstHeadCode, LastRepeatNumber, IncrementalDigits) + " at " +
14694  Utilities->Format96HHMM(GetRepeatTime(23, ActionVectorEntry.EventTime, y, IncMinutes));
14695  }
14696  }
14697  else if(ActionVectorEntry.SequenceType == Intermediate)
14698  {
14699  if(ActionVectorEntry.FormatType == TimeTimeLoc)
14700  {
14701  // here need 2 entries if times different so push the first right away & the second later
14702  // if times same just give the arrival entry
14703  if(ActionVectorEntry.DepartureTime != ActionVectorEntry.ArrivalTime)
14704  {
14705  PartStr = "Arrives at " + ActionVectorEntry.LocationName;
14706  TimeStr = Utilities->Format96HHMM(GetRepeatTime(4, ActionVectorEntry.ArrivalTime, y, IncMinutes));
14707  OneTTEntry.Action = PartStr;
14708  OneTTEntry.Time = TimeStr;
14709  OneTTTrain.OneFormattedTrainVector.push_back(OneTTEntry);
14710  PartStr = "Departs from " + ActionVectorEntry.LocationName;
14711  TimeStr = Utilities->Format96HHMM(GetRepeatTime(5, ActionVectorEntry.DepartureTime, y, IncMinutes));
14712  }
14713  else
14714  {
14715  PartStr = "Arrives & departs " + ActionVectorEntry.LocationName;
14716  TimeStr = Utilities->Format96HHMM(GetRepeatTime(29, ActionVectorEntry.ArrivalTime, y, IncMinutes));
14717  }
14718  }
14719  else if((ActionVectorEntry.FormatType == TimeLoc) && (ActionVectorEntry.ArrivalTime != TDateTime(-1)))
14720  {
14721  PartStr = "Arrives at " + ActionVectorEntry.LocationName;
14722  TimeStr = Utilities->Format96HHMM(GetRepeatTime(6, ActionVectorEntry.ArrivalTime, y, IncMinutes));
14723  }
14724  else if((ActionVectorEntry.FormatType == TimeLoc) && (ActionVectorEntry.ArrivalTime == TDateTime(-1)))
14725  {
14726  PartStr = "Departs from " + ActionVectorEntry.LocationName;
14727  TimeStr = Utilities->Format96HHMM(GetRepeatTime(7, ActionVectorEntry.DepartureTime, y, IncMinutes));
14728  }
14729  else if(ActionVectorEntry.FormatType == PassTime)
14730  {
14731  PartStr = "Passes " + ActionVectorEntry.LocationName;
14732  TimeStr = Utilities->Format96HHMM(GetRepeatTime(8, ActionVectorEntry.EventTime, y, IncMinutes));
14733  }
14734  else if(ActionVectorEntry.Command == "jbo")
14735  {
14736  PartStr = "Joined at " + ActionVectorEntry.LocationName + " by";
14737  TimeStr = GetRepeatHeadCode(37, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
14738  Utilities->Format96HHMM(GetRepeatTime(9, ActionVectorEntry.EventTime, y, IncMinutes));
14739  }
14740  else if(ActionVectorEntry.Command == "fsp")
14741  {
14742  PartStr = "Splits from front at " + ActionVectorEntry.LocationName + " to form";
14743  TimeStr = GetRepeatHeadCode(38, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
14744  Utilities->Format96HHMM(GetRepeatTime(10, ActionVectorEntry.EventTime, y, IncMinutes));
14745  }
14746  else if(ActionVectorEntry.Command == "rsp")
14747  {
14748  PartStr = "Splits from rear at " + ActionVectorEntry.LocationName + " to form";
14749  TimeStr = GetRepeatHeadCode(39, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
14750  Utilities->Format96HHMM(GetRepeatTime(11, ActionVectorEntry.EventTime, y, IncMinutes));
14751  }
14752  else if(ActionVectorEntry.Command == "cdt")
14753  {
14754  PartStr = "Changes direction at " + ActionVectorEntry.LocationName;
14755  TimeStr = Utilities->Format96HHMM(GetRepeatTime(12, ActionVectorEntry.EventTime, y, IncMinutes));
14756  }
14757  }
14758  else if(ActionVectorEntry.SequenceType == Finish)
14759  {
14760  if(ActionVectorEntry.Command == "Fns")
14761  {
14762  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
14763  TimeStr = GetRepeatHeadCode(40, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
14764  Utilities->Format96HHMM(GetRepeatTime(13, ActionVectorEntry.EventTime, y, IncMinutes));
14765  }
14766  else if(ActionVectorEntry.Command == "F-nshs")
14767  {
14768  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
14769  TimeStr = ActionVectorEntry.NonRepeatingShuttleLinkHeadCode + " at " + Utilities->Format96HHMM
14770  (GetRepeatTime(17, ActionVectorEntry.EventTime, y, IncMinutes));
14771  }
14772  else if((ActionVectorEntry.Command == "Fns-sh") && (y < (TrainDataEntry.NumberOfTrains - 1)))
14773  {
14774  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service ";
14775  TimeStr = GetRepeatHeadCode(41, ActionVectorEntry.OtherHeadCode, y + 1, IncDigits) + " at " +
14776  Utilities->Format96HHMM(GetRepeatTime(14, ActionVectorEntry.EventTime, y, IncMinutes));
14777  // y+1 because it's the NEXT service repeat number that is relevant
14778  }
14779  else if((ActionVectorEntry.Command == "Fns-sh") && (y >= (TrainDataEntry.NumberOfTrains - 1)))
14780  {
14781  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
14782  TimeStr = ActionVectorEntry.NonRepeatingShuttleLinkHeadCode + " at " + Utilities->Format96HHMM
14783  (GetRepeatTime(15, ActionVectorEntry.EventTime, y, IncMinutes));
14784  }
14785  else if((ActionVectorEntry.Command == "Frh-sh") && (y < (TrainDataEntry.NumberOfTrains - 1)))
14786  {
14787  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
14788  TimeStr = GetRepeatHeadCode(43, ActionVectorEntry.OtherHeadCode, y + 1, IncDigits) + " at " +
14789  Utilities->Format96HHMM(GetRepeatTime(16, ActionVectorEntry.EventTime, y, IncMinutes));
14790  // y+1 because it's the NEXT service repeat number that is relevant
14791  }
14792  else if((ActionVectorEntry.Command == "Frh-sh") && (y >= (TrainDataEntry.NumberOfTrains - 1)))
14793  {
14794  PartStr = "Terminates shuttle service at " + ActionVectorEntry.LocationName;
14795  // only used in chronological tt
14796  TimeStr = "End at " + Utilities->Format96HHMM(GetRepeatTime(28, ActionVectorEntry.EventTime, y, IncMinutes));
14797  // the "End at " is stripped out of the chronological tt but displayed in the traditional tt
14798  }
14799  else if(ActionVectorEntry.Command == "Frh")
14800  {
14801  PartStr = "Terminates at " + ActionVectorEntry.LocationName;
14802  // need here to examine the time of the preceding entry, may be ArrivalTime if TimeLoc, or EventTime otherwise
14803  if(z > 0)
14804  // should be for finish entry but include check for safety
14805  {
14806  if(ActionVector.at(z - 1).EventTime != TDateTime(-1))
14807  {
14808  TimeStr = Utilities->Format96HHMM(GetRepeatTime(30, ActionVector.at(z - 1).EventTime, y, IncMinutes));
14809  }
14810  else if(ActionVector.at(z - 1).ArrivalTime != TDateTime(-1))
14811  {
14812  TimeStr = Utilities->Format96HHMM(GetRepeatTime(31, ActionVector.at(z - 1).ArrivalTime, y, IncMinutes));
14813  }
14814  else
14815  TimeStr = " "; // shouldn't ever get here
14816  }
14817  }
14818  else if(ActionVectorEntry.Command == "Fer")
14819  {
14820  PartStr = "Exits railway" + GetExitLocationAndAt(0, ActionVectorEntry.ExitList);
14821  TimeStr = Utilities->Format96HHMM(GetRepeatTime(18, ActionVectorEntry.EventTime, y, IncMinutes));
14822  }
14823  else if(ActionVectorEntry.Command == "Fjo")
14824  {
14825  PartStr = "At " + ActionVectorEntry.LocationName + " joins";
14826  TimeStr = GetRepeatHeadCode(44, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
14827  Utilities->Format96HHMM(GetRepeatTime(19, ActionVectorEntry.EventTime, y, IncMinutes));
14828  }
14829  }
14830  else if(ActionVectorEntry.SequenceType == SequTypeForRepeatEntry)
14831  continue; // no entry needed for a repeat
14832  OneTTEntry.Action = PartStr;
14833  OneTTEntry.Time = TimeStr;
14834  OneTTTrain.OneFormattedTrainVector.push_back(OneTTEntry);
14835  // one per action
14836  }
14837  OneTTLine.OneCompleteFormattedTrainVector.push_back(OneTTTrain);
14838  // one per repeat
14839  }
14840  AllTTTrains->push_back(OneTTLine); // one per repeating train
14841  }
14842  // AllTTTrains vector now complete
14843 
14844  std::ofstream TTFile(TTFileName.c_str()); //formatted timetable
14845 
14846  if(TTFile == 0)
14847  {
14848  StopTTClockMessage(64, "Formatted timetable file failed to open - can't be created");
14849  delete AllTTTrains;
14850  Utilities->CallLogPop(1567);
14851  return;
14852  }
14853 
14854 /* formatted timetable types
14855  class TOneTrainFormattedEntry
14856  {
14857  AnsiString Action;//includes location if relevant
14858  AnsiString Time;
14859  };
14860 
14861  typedef std::vector<TOneTrainFormattedEntry> TOneFormattedTrainVector;
14862 
14863  class TOneCompleteFormattedTrain//headcode + list of actions
14864  {
14865  public:
14866  AnsiString HeadCode;
14867  TOneFormattedTrainVector OneFormattedTrainVector;
14868  };
14869 
14870  typedef std::vector<TOneCompleteFormattedTrain> TOneCompleteFormattedTrainVector;//list af all repeats
14871 
14872  class TTrainFormattedInformation//contains all information for a single TT entry (including repeats)
14873  {
14874  public:
14875  AnsiString Header;//description, mass, power, brake rate etc
14876  int NumberOfTrains;// number of repeats + 1
14877  TOneCompleteFormattedTrainVector OneCompleteFormattedTrainVector;//list af all repeats
14878  };
14879 
14880  typedef std::vector<TTrainFormattedInformation> TAllFormattedTrains;//all timetable in formatted form
14881  //end of formatted timetable types
14882 */
14883 
14884  // new layout using multiple rows
14885  TTFile << TableTitle.c_str() << '\n' << '\n';
14886  for(unsigned int x = 0; x < AllTTTrains->size(); x++)
14887  {
14888  TTFile << AllTTTrains->at(x).Header.c_str();
14889  TTFile << '\n';
14890  TTFile << ','; // for the blank line above the action list
14891  for(int y = 0; y < AllTTTrains->at(x).NumberOfTrains; y++) // number of repeating trains
14892  {
14893  if(y < (AllTTTrains->at(x).NumberOfTrains - 1))
14894  {
14895  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).HeadCode.c_str() << ',';
14896  }
14897  else
14898  {
14899  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).HeadCode.c_str();
14900  }
14901  }
14902  TTFile << '\n' << '\n';
14903 
14904  for(unsigned int z = 0; z < AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(0).OneFormattedTrainVector.size(); z++)
14905  {
14906  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(0).OneFormattedTrainVector.at(z).Action.c_str() << ',';
14907  for(int y = 0; y < AllTTTrains->at(x).NumberOfTrains; y++) // number of repeating trains
14908  {
14909  if(y < (AllTTTrains->at(x).NumberOfTrains - 1))
14910  {
14911  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Time.c_str() << ',';
14912  }
14913  else
14914  {
14915  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Time.c_str();
14916  }
14917  }
14918  TTFile << '\n';
14919  }
14920  TTFile << '\n' << '\n';
14921  }
14922 
14923  TTFile.close();
14924 
14925  AnsiString TTFileName2 = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
14926 
14927  TTFileName2 = CurDir + "\\Formatted timetables\\Timetable " + TTFileName2 + "; " + RailwayTitle + "; " + TimetableTitle + ".txt";
14928 
14929  std::ofstream TTFile2(TTFileName2.c_str()); //chronological timetable
14930 
14931  if(TTFile2 == 0)
14932  {
14933  StopTTClockMessage(67, "Chronological timetable file failed to open - can't be created");
14934  delete AllTTTrains;
14935  Utilities->CallLogPop(1710);
14936  return;
14937  }
14938 
14939  typedef std::multimap<AnsiString, AnsiString>TAnsiMultiMap;
14940  std::multimap<AnsiString, AnsiString>::iterator AMMIT;
14941  std::pair<AnsiString, AnsiString>AnsiMultiMapEntry;
14942 
14943  TAnsiMultiMap *TAMM = new TAnsiMultiMap;
14944  LastTTTime = ""; //records the very last time in the timetable - used in analysis file for Frh entries
14945 
14946  // multimap of AnsiStrings with TimeString as key (to sort automatically)
14947 
14948  TTFile2 << TableTitle.c_str() << '\n' << '\n';
14949  for(unsigned int x = 0; x < AllTTTrains->size(); x++)
14950  {
14951  for(int y = 0; y < AllTTTrains->at(x).NumberOfTrains; y++) // number of repeating trains
14952  {
14953  for(unsigned int z = 0; z < AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.size(); z++)
14954  {
14955  bool GiveMessagesFalse = false;
14956  AnsiString TimeString = AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Time;
14957  AnsiString HeadCodeString = AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).HeadCode;
14958  AnsiString ActionString = AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Action;
14959  if(CheckHeadCodeValidity(11, GiveMessagesFalse, TimeString.SubString(1, 4)))
14960  // 'NXNN at HH:MM' (will return true if H/C as integ check passed)
14961  { // fails for HH:MM because of ':' or 'End at HH:MM' because of ' '
14962  AnsiString OtherHeadCode = TimeString.SubString(1, 4);
14963  TimeString = TimeString.SubString(9, 5);
14964  ActionString += " " + OtherHeadCode;
14965  }
14966  if(TimeString.SubString(1, 7) == "End at ")
14967  // for Frh-sh final entry
14968  {
14969  TimeString = TimeString.SubString(8, 5);
14970  }
14971  AnsiString OneLine = TimeString + ' ' + HeadCodeString + ' ' + ActionString + '\n';
14972  AnsiMultiMapEntry.first = TimeString;
14973  AnsiMultiMapEntry.second = OneLine;
14974  TAMM->insert(AnsiMultiMapEntry);
14975  }
14976  }
14977  }
14978 
14979  for(AMMIT = TAMM->begin(); AMMIT != TAMM->end(); AMMIT++)
14980  {
14981  TTFile2 << (AMMIT->second).c_str();
14982  }
14983  delete AllTTTrains;
14984  delete TAMM;
14985  TTFile2.close();
14986  Utilities->CallLogPop(1580);
14987 }
14988 
14989 // ---------------------------------------------------------------------------
14990 
14991 bool TTrainController::CreateTTAnalysisFile(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir, bool ArrChecked, bool DepChecked,
14992  bool AtLocChecked, int ArrRange, int DepRange)
14993 {
14994 
14995  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CreateTTAnalysisFile");
14996  bool AnalysisError = false;
14997 
14998  try
14999  {
15000  //New section after v2.4.3 for tt conflict analysis
15001  /*
15002  typedef std::list<AnsiString> TServiceCallingLocsList;
15003  typedef std::vector<TServiceCallingLocsList> TAllServiceCallingLocsMap;
15004 
15005  struct TLocServiceTimes
15006  {
15007  AnsiString Location;
15008  AnsiString ServiceAndRepeatNum;
15009  AnsiString AtLocTime;
15010  AnsiString ArrTime;
15011  AnsiString DepTime;
15012  };
15013  typedef std::vector<TLocServiceTimes> TLocServiceTimesVector;
15014  */
15015 
15016  //first have to check through all the services and give each one a unique name, or the analysis won't recognise differences between services that have the same reference
15017  //to do that need a new TrainDataVector as don't want to change anything in the original. TrainDataVectorCopy is used for building AllServiceCallingLocsMap & LocServiceTimesVector
15018 
15019  TTrainDataVector TrainDataVectorCopy = TrainDataVector; //don't need it on heap as TrainController is on the heap. Didn't need others in CreatFormattedTimetables but leave as is.
15020  TTrainDataVector::iterator TDVIt, TDVCopyIt;
15021  int Suffix = 0;
15022  int IteratorNumber = 0;
15023  AnsiString AnsiSuffix = "";
15024  for(TDVIt = TrainDataVector.begin(); TDVIt != TrainDataVector.end() - 1; TDVIt++)
15025  {
15026  IteratorNumber++; //first value in loop is 1
15027  Suffix = 0;
15028  for(TDVCopyIt = TrainDataVectorCopy.begin() + IteratorNumber; TDVCopyIt != TrainDataVectorCopy.end(); TDVCopyIt++)
15029  {
15030  if(TDVCopyIt->ServiceReference == TDVIt->ServiceReference)
15031  {
15032  Suffix++; //first value is 1
15033  AnsiSuffix = AnsiString(Suffix);
15034  TDVCopyIt->ServiceReference = TDVIt->ServiceReference + "/" + AnsiSuffix;
15035  }
15036  }
15037  }
15038 
15039  //build AllServiceCallingLocsMap, it only uses the base service reference (with /1, /2 etc suffixes) as later times are calculated from the repeat number
15040  TServiceCallingLocsList ServiceCallingLocsList;
15041  std::pair<AnsiString, TServiceCallingLocsList> AllServiceCallingLocsEntry;
15042  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
15043  {
15044  const TTrainDataEntry &TrainDataEntry = TrainDataVectorCopy.at(x);
15045  const TActionVector &ActionVector = TrainDataEntry.ActionVector;
15046  AllServiceCallingLocsEntry.first = TrainDataEntry.ServiceReference;
15047  ServiceCallingLocsList.clear();
15048  if(ActionVector.empty())
15049  {
15050  continue;
15051  }
15052  if(ActionVector.at(0).SignallerControl)
15053  {
15054  continue;
15055  }
15056  for(unsigned int z = 0; z < ActionVector.size(); z++)
15057  {
15058  TActionVectorEntry AVE = ActionVector.at(z);
15059  if(AVE.FormatType == StartNew)
15060  {
15061  if(AVE.LocationType == AtLocation) //located Snt
15062  {
15063  ServiceCallingLocsList.push_back(AVE.LocationName);
15064  }
15065  else //unlocated Snt (could be entering at continuation)
15066  {
15068  if(TE.ActiveTrackElementName != "")
15069  {
15070  ServiceCallingLocsList.push_back(TE.ActiveTrackElementName);
15071  }
15072  else
15073  {
15074  int HLoc = TE.HLoc;
15075  int VLoc = TE.VLoc;
15076  AnsiString HString;
15077  AnsiString VString;
15078  if(HLoc < 0)
15079  {
15080  HString = 'N' + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
15081  }
15082  else
15083  {
15084  HString = AnsiString(HLoc);
15085  }
15086  if(VLoc < 0)
15087  {
15088  VString = 'N' + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
15089  }
15090  else
15091  {
15092  VString = AnsiString(VLoc);
15093  }
15094  ServiceCallingLocsList.push_back(HString + '-' + VString);
15095  }
15096  }
15097  }
15098  else if(AVE.SequenceType == Start) //other start entries, all located
15099  {
15100  ServiceCallingLocsList.push_back(AVE.LocationName);
15101  }
15102  else if(AVE.FormatType == TimeLoc) //z must be > 0
15103  {
15104  if(ServiceCallingLocsList.back() != AVE.LocationName)
15105  {
15106  ServiceCallingLocsList.push_back(AVE.LocationName); //may be listed twice in succession so only want one entry
15107  }
15108  }
15109  else if(AVE.FormatType == PassTime)
15110  {
15111  ServiceCallingLocsList.push_back(AVE.LocationName);
15112  }
15113  else if(AVE.FormatType == TimeTimeLoc)
15114  {
15115  ServiceCallingLocsList.push_back(AVE.LocationName);
15116  }
15117  else if(AVE.Command == "cdt") //list if not next to start or finish
15118  {
15119  if(ActionVector.at(z-1).SequenceType == Start)
15120  {
15121  continue;
15122  }
15123  else if(ActionVector.at(z+1).SequenceType == Finish) //although deal with Fer entries cdt (train stopped) can't precede FER (train moving)
15124  {
15125  continue;
15126  }
15127  else
15128  {
15129  AnsiString TimeString = Utilities->Format96HHMM(AVE.EventTime);
15130  ServiceCallingLocsList.push_back("%%%" + TimeString); //%%% is a marker - unlikely that any locations will begin with this & easy to check to identify a time
15131  }
15132  }
15133  else if(AVE.FormatType == ExitRailway) //Fer
15134  {
15135  TTrackElement TE = Track->TrackElementAt(995, AVE.ExitList.front());
15136  AnsiString LName = TE.ActiveTrackElementName;
15137  if(LName != "")
15138  {
15139  ServiceCallingLocsList.push_back(LName);
15140  }
15141  else
15142  {
15143  int HLoc = TE.HLoc;
15144  int VLoc = TE.VLoc;
15145  AnsiString HString;
15146  AnsiString VString;
15147  if(HLoc < 0)
15148  {
15149  HString = 'N' + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
15150  }
15151  else
15152  {
15153  HString = AnsiString(HLoc);
15154  }
15155  if(VLoc < 0)
15156  {
15157  VString = 'N' + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
15158  }
15159  else
15160  {
15161  VString = AnsiString(VLoc);
15162  }
15163  ServiceCallingLocsList.push_back(HString + '-' + VString);
15164  }
15165  }
15166  }
15167  AllServiceCallingLocsEntry.second = ServiceCallingLocsList;
15168  AllServiceCallingLocsMap.insert(AllServiceCallingLocsEntry);
15169  }
15170  //AllServiceCallingLocsMap built
15171 
15172  //test validity of AllServiceCallingLocsMap
15173 /*
15174  AnsiString TestFile = CurDir + "\\Formatted timetables\\TestFile; " + RailwayTitle + "; " + TimetableTitle + ".txt";
15175  std::ofstream Test(TestFile.c_str());
15176 
15177  if(TestFile == 0)
15178  {
15179  ShowMessage("TestFile failed to open - can't be created");
15180  Utilities->CallLogPop();
15181  return false;
15182  }
15183 
15184  for(TAllServiceCallingLocsMap::iterator ASCLIt = AllServiceCallingLocsMap.begin(); ASCLIt != AllServiceCallingLocsMap.end(); ASCLIt++)
15185  {
15186  Test << ASCLIt->first << '\n'; //service ref
15187  for(TServiceCallingLocsList::iterator SCLIt = ASCLIt->second.begin(); SCLIt != ASCLIt->second.end(); SCLIt++)
15188  {
15189  Test << *SCLIt << '\n';
15190  }
15191  Test << "\n\n";
15192  }
15193  Test.close();
15194  Utilities->CallLogPop();
15195  return true;
15196 */
15197 
15198  //initialise variables before calc LastTTTime & build LocServiceTimesVector
15199  if(TrainDataVector.empty())
15200  {
15201  ShowMessage("Unable to create a program-readable timetable - please check the timetable file validity");
15202  Utilities->CallLogPop(2209);
15203  return false;
15204  }
15205  TLocServiceTimes TLSTEntry;
15206  TLocServiceTimesVector LocServiceTimesVector; //will be on heap as TrainController is on the heap
15207  bool NumPlatsAtThisLocCalculated = false, ArrivalsPrinted = false, DeparturesPrinted = false, AtLocsPrinted = false;
15208  AnsiString PreviousService = "", PreviousServiceAndRepeatNumTotalOutput = "", BasicTime = "", MinuteString = "", LastAnsiTime = "";
15209  int NumTrains = 0, NumPlats = 0, LastFrhCount = 0, FrhCount = 0, NumTrainsAtLoc = 0;
15210  LastTTTime = "";
15211 
15212  //calculate LastTTTime
15213  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
15214  {
15215  TTrainDataEntry &TrainDataEntry = TrainDataVectorCopy.at(x);
15216  TActionVector &ActionVector = TrainDataEntry.ActionVector;
15217  TActionVectorIterator AVLast = ActionVector.end() - 1;//points to last entry
15218  TDateTime LastTDTime;
15219  int IncMinutes = 0;
15220  NumTrains = TrainDataEntry.NumberOfTrains;
15221  if(ActionVector.empty())
15222  {
15223  continue;
15224  }
15225  if(ActionVector.at(0).SignallerControl)
15226  {
15227  continue;
15228  }
15229  if(AVLast->FormatType == Repeat)
15230  {
15231  IncMinutes = ActionVector.at(ActionVector.size() - 1).RearStartOrRepeatMins;
15232  AVLast--; //now points to the command before the repeat
15233  }
15234  if(AVLast->FormatType == FinRemHere) //not 'else if' as may have both a repeat and an Frh
15235  {
15236  AVLast--; //points to last timed entry
15237  }
15238  //here AVLast points to last entry with a time
15239  if(AVLast->ArrivalTime != TDateTime(-1))
15240  {
15241  LastTDTime = AVLast->ArrivalTime;
15242  }
15243  else if(AVLast->EventTime != TDateTime(-1)) //can't be a departure time
15244  {
15245  LastTDTime = AVLast->EventTime;
15246  }
15247  else
15248  {
15249  continue; //shouldn't ever reach here but if do then skip this service
15250  }
15251  if(NumTrains == 1)
15252  {
15253  LastAnsiTime = Utilities->Format96HHMM(LastTDTime);
15254  }
15255  else
15256  {
15257  LastAnsiTime = Utilities->Format96HHMM(GetRepeatTime(59, LastTDTime, NumTrains - 1, IncMinutes));
15258  }
15259  if(LastAnsiTime > LastTTTime)
15260  {
15261  LastTTTime = LastAnsiTime;
15262  }
15263  }
15264 
15265 //build LocServiceTimesVector
15266 
15267 /* struct TLocServiceTimes
15268  {
15269  AnsiString Location;
15270  AnsiString ServiceAndRepeatNum;
15271  AnsiString AtLocTime;
15272  AnsiString ArrTime;
15273  AnsiString DepTime;
15274  };
15275  typedef std::vector<TLocServiceTimes> TLocServiceTimesVector;
15276 
15277 This works as follows:
15278 ServiceAndRepeatNum is taken from the TrainDataVector as it is the same for all actionvector entries
15279 Location is taken from ActionVectorEntry.LocationName if there is one, or from the H & V locations if not (e.g. at an unnamed Fer)
15280 AtLocTime is always entered either on its own or with ArrTime or DepTime as appropriate
15281 
15282 Every action for every train is examined and times entered as follows:-
15283 a) a located Snt: entry time becomes the AtLocTime, and all subsequent minutes entered too up to but not including a departure or a finish
15284 b) an unlocated Snt: entry time becomes DepTime
15285 c) all other start entries: entry time becomes AtLoc, and all subsequent minutes entered too up to but not including a departure or a finish
15286 d) TimeLoc Arr: entry time becomes ArrTime, and all subsequent minutes entered too up to but not including a departure or a finish
15287 e) TimeLoc Dep: entry time becomes DepTime, checks if DepTime same as earlier ArrTime and if so all times go in as one entry
15288 f) TimeTimeLoc: Arrival time entered as ErrTime, a check if Arr & Dep same and if s go in as one entry, else all minutes between entered as AtLocs then DepTime
15289 g) ExitRailway (Fer): check if located and use LocationName if so. else use H & V positions, time becomes AtLocTime
15290 h) Frh: use the earlier vector time as the AtLocTime and set FrhMarker, and enter all minutes to end of timetable as AtLocs
15291 i) Frh-sh: for the last train use time as AtLocTime, set FrhMarker, and enter all minutes to end of timetable as AtLocs
15292 j) all other finish entries (all link to another service) are ignored as will be listed for the linked service
15293 */
15294  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
15295  {
15296  const TTrainDataEntry &TrainDataEntry = TrainDataVectorCopy.at(x);
15297  const TActionVector &ActionVector = TrainDataEntry.ActionVector;
15298  AnsiString ServiceRef = TrainDataEntry.ServiceReference;
15299  int IncMinutes = 0;
15300  NumTrains = TrainDataEntry.NumberOfTrains;
15301  if(ActionVector.empty())
15302  {
15303  continue;
15304  }
15305  if(ActionVector.at(0).SignallerControl)
15306  {
15307  continue;
15308  }
15309  if(ActionVector.at(ActionVector.size() - 1).FormatType == Repeat)
15310  {
15311  IncMinutes = ActionVector.at(ActionVector.size() - 1).RearStartOrRepeatMins;
15312  }
15313 
15314  for(int y = 0; y < NumTrains; y++)
15315  {
15316  if(NumTrains == 1)
15317  {
15318  TLSTEntry.ServiceAndRepeatNum = ServiceRef;
15319  }
15320  else if(y == 0)
15321  {
15322  TLSTEntry.ServiceAndRepeatNum = ServiceRef + " (First service)";
15323  }
15324  else
15325  {
15326  TLSTEntry.ServiceAndRepeatNum = ServiceRef + " (Repeat " + AnsiString(y) + ")";
15327  }
15328  for(unsigned int z = 0; z < ActionVector.size(); z++)
15329  {
15330  TActionVectorEntry AVE = ActionVector.at(z);
15331  TLSTEntry.AtLocTime = "";
15332  TLSTEntry.ArrTime = "";
15333  TLSTEntry.DepTime = "";
15334  TLSTEntry.Location = "";
15335  TLSTEntry.FrhMarker = "";
15336 
15337  if(AVE.FormatType == StartNew) //Snt only
15338  {
15339  if(AVE.LocationType == AtLocation) //located Snt, class time as AtLocTime
15340  {
15341  TLSTEntry.Location = AVE.LocationName;
15342  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(58, AVE.EventTime, y, IncMinutes));
15343  LocServiceTimesVector.push_back(TLSTEntry);
15344 
15345  //now look forwards until find a departure or a Fns, Fns-sh etc & add in all the minutes up to but not including the dep or finish times
15346  AnsiString IncTime = "", FoundStopTime = ""; //these handled in later checks
15347  for(unsigned int a = z + 1; a < ActionVector.size(); a++)
15348  {
15349  if(ActionVector.at(a).FormatType == TimeLoc) //must be a departure
15350  {
15351  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(62, ActionVector.at(a).DepartureTime, y, IncMinutes));
15352  break;
15353  }
15354  if(ActionVector.at(a).SequenceType == Finish) //finish catered in a later test
15355  {
15356  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(63, ActionVector.at(a).EventTime, y, IncMinutes));
15357  break;
15358  }
15359  }
15360  if(FoundStopTime == "")
15361  {
15362  throw Exception("Failure to determine FoundStopTime for located Snt");
15363  }
15364  int WhileCount = 0;
15365  while(true)
15366  {
15367  //add minutes until reach FoundStopTime but don't add that time
15368  WhileCount++;
15369  IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
15370  TLSTEntry.AtLocTime = IncTime;//all entered times will be AtLocs
15371  TLSTEntry.DepTime = "";
15372  TLSTEntry.ArrTime = "";
15373  if(IncTime >= FoundStopTime) //don't add that time
15374  {
15375  break;
15376  }
15377  LocServiceTimesVector.push_back(TLSTEntry);
15378  if(WhileCount > 2000)
15379  {
15380  throw Exception("While loop failed to break in 2000 loops for located Snt");
15381  }
15382  }
15383  }
15384  else //unlocated Snt, use the EventTime as DepTime for this vector
15385  {
15387  if(TE.ActiveTrackElementName != "")
15388  {
15389  TLSTEntry.Location = TE.ActiveTrackElementName;
15390  }
15391  else
15392  {
15393  int HLoc = TE.HLoc;
15394  int VLoc = TE.VLoc;
15395  AnsiString HString;
15396  AnsiString VString;
15397  if(HLoc < 0)
15398  {
15399  HString = 'N' + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
15400  }
15401  else
15402  {
15403  HString = AnsiString(HLoc);
15404  }
15405  if(VLoc < 0)
15406  {
15407  VString = 'N' + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
15408  }
15409  else
15410  {
15411  VString = AnsiString(VLoc);
15412  }
15413  TLSTEntry.Location = HString + '-' + VString;
15414  }
15415  TLSTEntry.DepTime = Utilities->Format96HHMM(GetRepeatTime(49, AVE.EventTime, y, IncMinutes));
15416  TLSTEntry.AtLocTime = TLSTEntry.DepTime;
15417  LocServiceTimesVector.push_back(TLSTEntry);
15418  }
15419  }
15420 
15421  else if(AVE.SequenceType == Start) //other start entries, all located
15422  {
15423  TLSTEntry.Location = AVE.LocationName;
15424  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(50, AVE.EventTime, y, IncMinutes));
15425  LocServiceTimesVector.push_back(TLSTEntry);
15426  //now look forwards until find a departure or a Fns, Fns-sh etc & add in all the minutes up to but not including the dep or finish times
15427  AnsiString IncTime = "", FoundStopTime = ""; //these handled in other checks
15428  for(unsigned int a = z + 1; a < ActionVector.size(); a++)
15429  {
15430  if(ActionVector.at(a).FormatType == TimeLoc) //must be a departure
15431  {
15432  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(64, ActionVector.at(a).DepartureTime, y, IncMinutes));
15433  break;
15434  }
15435  if(ActionVector.at(a).SequenceType == Finish) //finish catered in a later test
15436  {
15437  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(65, ActionVector.at(a).EventTime, y, IncMinutes));
15438  break;
15439  }
15440  }
15441  if(FoundStopTime == "")
15442  {
15443  throw Exception("Failure to determine FoundStopTime for SequenceType == Start");
15444  }
15445  int WhileCount = 0;
15446  while(true)
15447  {
15448  //add minutes until reach FoundStopTime but don't add that time
15449  WhileCount++;
15450  IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
15451  TLSTEntry.AtLocTime = IncTime;//all entered times will be AtLocs
15452  TLSTEntry.DepTime = "";
15453  TLSTEntry.ArrTime = "";
15454  if(IncTime >= FoundStopTime) //don't add that time
15455  {
15456  break;
15457  }
15458  LocServiceTimesVector.push_back(TLSTEntry);
15459  if(WhileCount > 2000)
15460  {
15461  throw Exception("While loop failed to break in 2000 loops for SequenceType == Start");
15462  }
15463  }
15464  }
15465 
15466  else if(AVE.FormatType == TimeLoc) //could be arr or dep, if ar5rival add in all mins to the departure or finish
15467  {
15468  TLSTEntry.Location = AVE.LocationName;
15469  if(AVE.ArrivalTime > TDateTime(-1)) //one or other set, not both, in this case arrival
15470  {
15471  bool SkipAddingMinutes = false;
15472  TLSTEntry.ArrTime = Utilities->Format96HHMM(GetRepeatTime(51, AVE.ArrivalTime, y, IncMinutes));
15473  TLSTEntry.AtLocTime = TLSTEntry.ArrTime;
15474  LocServiceTimesVector.push_back(TLSTEntry); //Arr and AtLoc added (may be popped if dep time found to be same at next TimeLoc)
15475  //now look forwards until find a departure or a Fns, Fns-sh etc & add in all the minutes up to but not including the dep or finish times
15476  AnsiString IncTime = "", FoundStopTime = ""; //these handled in other checks
15477  for(unsigned int a = z + 1; a < ActionVector.size(); a++)
15478  {
15479  if(ActionVector.at(a).FormatType == TimeLoc) //must be a departure
15480  {
15481  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(66, ActionVector.at(a).DepartureTime, y, IncMinutes));
15482  break;
15483  }
15484  if(ActionVector.at(a).SequenceType == Finish) //finish catered in a later test
15485  {
15486  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(67, ActionVector.at(a).EventTime, y, IncMinutes));
15487  if((a == z + 1) && (FoundStopTime == TLSTEntry.ArrTime) && ((ActionVector.at(a).LinkedTrainEntryPtr > 0) || (ActionVector.at(a).NonRepeatingShuttleLinkEntryPtr > 0)))
15488  //finish immediately after arrival at same time, and a forward linked service. Added at v2.6.0 to prevent two linked trains being listed at same location
15489  {
15490  LocServiceTimesVector.pop_back();//pop the entry as the linked train will be listed at the relevant time and don't want to list both
15491  SkipAddingMinutes = true;
15492  }
15493  break;
15494  }
15495  }
15496  if(FoundStopTime == "")
15497  {
15498  throw Exception("Failure to determine FoundStopTime for SequenceType == Start");
15499  }
15500  if(!SkipAddingMinutes)
15501  {
15502  int WhileCount = 0;
15503  while(true)
15504  {
15505  //add minutes until reach FoundStopTime but don't add that time
15506  WhileCount++;
15507  IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
15508  TLSTEntry.AtLocTime = IncTime;//all entered times will be AtLocs
15509  TLSTEntry.DepTime = "";
15510  TLSTEntry.ArrTime = "";
15511  if(IncTime >= FoundStopTime) //don't add that time
15512  {
15513  break;
15514  }
15515  LocServiceTimesVector.push_back(TLSTEntry);
15516  if(WhileCount > 2000)
15517  {
15518  throw Exception("While loop failed to break in 2000 loops for SequenceType == Start");
15519  }
15520  }
15521  }
15522  }
15523  else if(AVE.DepartureTime > TDateTime(-1)) //need to check if the arrival time (which should already be listed) is same and if so put all times on one line
15524  {
15525  TLSTEntry.DepTime = Utilities->Format96HHMM(GetRepeatTime(52, AVE.DepartureTime, y, IncMinutes));
15526  TLSTEntry.AtLocTime = TLSTEntry.DepTime;
15527  if((TLSTEntry.Location == LocServiceTimesVector.back().Location) && (TLSTEntry.ServiceAndRepeatNum == LocServiceTimesVector.back().ServiceAndRepeatNum))//if not it's a new service
15528  {
15529  if(TLSTEntry.DepTime == LocServiceTimesVector.back().ArrTime)
15530  {
15531  TLSTEntry.ArrTime = LocServiceTimesVector.back().ArrTime;
15532  LocServiceTimesVector.pop_back();
15533  LocServiceTimesVector.push_back(TLSTEntry); //Arr, Dep and AtLoc added in place of earlier Arr entry.
15534  }
15535  else //just add the dep & atloc times
15536  {
15537  TLSTEntry.ArrTime = "";
15538  LocServiceTimesVector.push_back(TLSTEntry);
15539  }
15540  }
15541  else //just add the dep & atloc times
15542  {
15543  TLSTEntry.ArrTime = "";
15544  LocServiceTimesVector.push_back(TLSTEntry);
15545  }
15546  }
15547  }
15548 
15549  else if(AVE.FormatType == TimeTimeLoc)
15550  {
15551  TLSTEntry.Location = AVE.LocationName;
15552  if(AVE.ArrivalTime > TDateTime(-1)) //should be
15553  {
15554  TLSTEntry.ArrTime = Utilities->Format96HHMM(GetRepeatTime(53, AVE.ArrivalTime, y, IncMinutes));
15555  TLSTEntry.AtLocTime = TLSTEntry.ArrTime;
15556  }
15557  if(AVE.DepartureTime > TDateTime(-1)) //should be
15558  {
15559  TLSTEntry.DepTime = Utilities->Format96HHMM(GetRepeatTime(54, AVE.DepartureTime, y, IncMinutes));
15560  }
15561  if(TLSTEntry.ArrTime == TLSTEntry.DepTime)
15562  {
15563  LocServiceTimesVector.push_back(TLSTEntry);
15564  }
15565  else
15566  {
15567  AnsiString TempDepTime = TLSTEntry.DepTime; //save it temporarily
15568  TLSTEntry.DepTime = "";
15569  LocServiceTimesVector.push_back(TLSTEntry); //push just the arrival and AtLoc times
15570  TLSTEntry.ArrTime = ""; //done with this now
15571  while(TLSTEntry.AtLocTime < TempDepTime)
15572  {
15573  TLSTEntry.AtLocTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
15574  if(TLSTEntry.AtLocTime == TempDepTime)
15575  {
15576  TLSTEntry.DepTime = TempDepTime; //restore value
15577  LocServiceTimesVector.push_back(TLSTEntry); //push the AtLoc and Dep times - will finish loop after this
15578  }
15579  else
15580  {
15581  LocServiceTimesVector.push_back(TLSTEntry); //push the AtLoc time on its own
15582  }
15583  }
15584  }
15585  }
15586 
15587  else if(AVE.FormatType == ExitRailway) //Fer
15588  {
15589  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(55, AVE.EventTime, y, IncMinutes));
15590  AnsiString LName = Track->TrackElementAt(990, AVE.ExitList.front()).ActiveTrackElementName;
15591  if(LName != "")
15592  {
15593  TLSTEntry.Location = LName;
15594  }
15595  else
15596  {
15597  int HLoc = Track->TrackElementAt(991, AVE.ExitList.front()).HLoc;
15598  int VLoc = Track->TrackElementAt(992, AVE.ExitList.front()).VLoc;
15599  AnsiString HString;
15600  AnsiString VString;
15601  if(HLoc < 0)
15602  {
15603  HString = 'N' + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
15604  }
15605  else
15606  {
15607  HString = AnsiString(HLoc);
15608  }
15609  if(VLoc < 0)
15610  {
15611  VString = 'N' + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
15612  }
15613  else
15614  {
15615  VString = AnsiString(VLoc);
15616  }
15617  TLSTEntry.Location = HString + '-' + VString;
15618  }
15619  LocServiceTimesVector.push_back(TLSTEntry); //just use the exit time as AtLocTime
15620  }
15621 
15622  else if(AVE.FormatType == FinRemHere) //Frh, not Frh-sh, that dealt with next
15623  {
15624  AnsiString FrhTime;
15625  if(ActionVector.at(z - 1).ArrivalTime != TDateTime(-1))
15626  {
15627  FrhTime = Utilities->Format96HHMM(GetRepeatTime(56, ActionVector.at(z - 1).ArrivalTime, y, IncMinutes));
15628  }
15629  else if(ActionVector.at(z - 1).EventTime != TDateTime(-1))
15630  {
15631  FrhTime = Utilities->Format96HHMM(GetRepeatTime(57, ActionVector.at(z - 1).EventTime, y, IncMinutes));
15632  }
15633  TLSTEntry.AtLocTime = FrhTime; //use the last entry time as the first recorded time
15634  TLSTEntry.Location = AVE.LocationName;
15635  AnsiString IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
15636  TLSTEntry.FrhMarker = "Frh";
15637  LocServiceTimesVector.push_back(TLSTEntry);
15638  TLSTEntry.FrhMarker = "";
15639  //add all times from next minute to end of timetable
15640  while(IncTime <= LastTTTime)
15641  {
15642  TLSTEntry.AtLocTime = IncTime;
15643  LocServiceTimesVector.push_back(TLSTEntry);
15644  IncTime = Utilities->IncrementAnsiTimeOneMinute(IncTime);
15645  }
15646  }
15647 
15648  else if(AVE.Command == "Frh-sh") //do nothing if links to other shuttle but treat as Frh when remaining here
15649  {
15650  if(y == NumTrains - 1)//last repeat, it remains here when accessed for the last train
15651  {
15652  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(68, AVE.EventTime, y, IncMinutes));
15653  TLSTEntry.Location = AVE.LocationName;
15654  AnsiString IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
15655  TLSTEntry.FrhMarker = "Frh";
15656  LocServiceTimesVector.push_back(TLSTEntry);
15657  TLSTEntry.FrhMarker = "";
15658  //add all times from next minute to end of timetable
15659  while(IncTime <= LastTTTime)
15660  {
15661  TLSTEntry.AtLocTime = IncTime;
15662  LocServiceTimesVector.push_back(TLSTEntry);
15663  IncTime = Utilities->IncrementAnsiTimeOneMinute(IncTime);
15664  }
15665  }
15666  }
15667 
15668  else if(AVE.SequenceType == Finish) //other finish types - all located & all link to another service
15669  {
15670  //nothing is done here as the entry will be listed at this time under the new service reference
15671  }
15672  }
15673  }
15674  }
15675 
15676  //now sort in location order
15677  std::sort(LocServiceTimesVector.begin(), LocServiceTimesVector.end(), &LocServiceTimesLocationSort); //LocServiceTimesLocationSort is a function pointer
15678  //LocServiceTimesVector now complete & sorted in location order
15679 
15680  //declare pointers for use in printouts
15681  TLocServiceTimesVector::iterator Ptr1, Ptr2;
15682 
15683  //set up the output file
15684  AnsiString TTFileName3 = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
15685  TTFileName3 = CurDir + "\\Formatted timetables\\Conflict Analysis " + TTFileName3 + "; " + RailwayTitle + "; " + TimetableTitle + ".csv";
15686 
15687  std::ofstream TTFile3(TTFileName3.c_str());
15688 
15689  if(TTFile3 == 0)
15690  {
15691  ShowMessage("Conflict Analysis file failed to open - can't be created");
15692  Utilities->CallLogPop(2210);
15693  return false;
15694  }
15695 
15696  if(LocServiceTimesVector.empty())
15697  {
15698  ShowMessage("No timetabled services found");
15699  TTFile3.close();
15700  DeleteFile(TTFileName3);
15701  Utilities->CallLogPop(2211);
15702  return false;
15703  }
15704 
15705  TTFile3 << "Timetable analysis for timetable: '" + TimetableTitle + "' in conjunction with railway: '" + RailwayTitle + "'\n\n\n";
15706 
15707 
15708  //arrivals
15709  if(ArrChecked)
15710  {
15711  //sort in ArrTime order for each location
15712  Ptr1 = LocServiceTimesVector.begin();
15713  Ptr2 = Ptr1 + 1;
15714  while(Ptr2 != LocServiceTimesVector.end())
15715  {
15716  while(Ptr2->Location == Ptr1->Location) //ends with Ptr2 one past same Location value as Ptr1
15717  {
15718  Ptr2++;
15719  if(Ptr2 == LocServiceTimesVector.end())
15720  {
15721  break;
15722  }
15723  }
15724  std::sort(Ptr1, Ptr2, &LocServiceTimesArrTimeSort);
15725  Ptr1 = Ptr2; //first entry with next name
15726  if(Ptr2 != LocServiceTimesVector.end())
15727  {
15728  Ptr2++;
15729  }
15730  }
15731 
15732  //routine for arrivals - number of trains arriving within the specified range with services listed at the end
15733 
15734  TTFile3 << "Arrival analysis: an asterisk means that the number of same approach code arrivals is equal to or greater than the number of platforms.\n";
15735  TTFile3 << "If the total number of arrivals exceeds the number of platforms the 'Trains present at location analysis' will show an asterisk.\n\n";
15736  MinuteString = " minutes";
15737  AnsiString ServiceAndRepeatNumTotal = "", ServiceAndRepeatNumTotalOutput = "";
15738  if(ArrRange == 1)
15739  {
15740  MinuteString = " minute";
15741  }
15742  TTFile3 << "Location,Number of,Number of,Services arriving within " << AnsiString(ArrRange) << MinuteString << " with their arrival times and approach codes\n";
15743  TTFile3 << ",Platforms,Trains\n\n";
15744 
15745  Ptr1 = LocServiceTimesVector.begin();
15746  Ptr2 = Ptr1 + 1;
15747  while(Ptr2 != LocServiceTimesVector.end())
15748  {
15749  PreviousService = "";
15750  NumTrainsAtLoc = 0;
15751  ServiceAndRepeatNumTotal = "";
15752  NumPlats = 0;
15753  NumPlatsAtThisLocCalculated = false;
15754  BasicTime = "";
15755  while((Ptr2->Location != Ptr1->Location) || ((Ptr1->Location == "") && (Ptr2->Location == "")))
15756  {
15757  PreviousService = "";
15758  NumTrainsAtLoc = 0;
15759  ServiceAndRepeatNumTotal = "";
15760  NumPlats = 0;
15761  NumPlatsAtThisLocCalculated = false;
15762  BasicTime = "";
15763  Ptr1++;
15764  Ptr2++;
15765  if(Ptr2 == LocServiceTimesVector.end())
15766  {
15767  break;
15768  }
15769  }
15770  if(Ptr2 == LocServiceTimesVector.end())
15771  {
15772  break;
15773  }
15774  while(Ptr2->Location == Ptr1->Location)
15775  {
15776  PreviousService = "";
15777  NumTrainsAtLoc = 0;
15778  ServiceAndRepeatNumTotal = "";
15779  BasicTime = Ptr1->ArrTime; //used to compare later times - later pointer contents have same or later times as sorted in time order
15780  if((Ptr1->Location == "") && (Ptr2->Location == ""))
15781  {
15782  break;
15783  }
15784  while(!WithinTimeRange(0, BasicTime, Ptr2->ArrTime, ArrRange) || ((Ptr1->ArrTime == "") && (Ptr2->ArrTime == "")))
15785  {
15786  BasicTime = Ptr2->ArrTime; //used to compare later times or last can exceed first
15787  Ptr1++;
15788  Ptr2++;
15789  if(Ptr2 == LocServiceTimesVector.end())
15790  {
15791  break;
15792  }
15793  if(Ptr2->Location != Ptr1->Location)
15794  {
15795  break;
15796  }
15797  }
15798  if(Ptr2 == LocServiceTimesVector.end())
15799  {
15800  break;
15801  }
15802  if(Ptr2->Location != Ptr1->Location)
15803  {
15804  break;
15805  }
15806  while(WithinTimeRange(1, BasicTime, Ptr2->ArrTime, ArrRange))
15807  {
15808  if((Ptr1->ArrTime == "") && (Ptr2->ArrTime == ""))
15809  {
15810  break;
15811  }
15812  if(!NumPlatsAtThisLocCalculated) //num plats at relevant location, reset when locations change
15813  {
15814  NumPlats = Track->NumberOfPlatforms(0, Ptr1->Location);
15815  NumPlatsAtThisLocCalculated = true;
15816  }
15817  if(Ptr1->ServiceAndRepeatNum != PreviousService) //don't print it twice if same as last - as will be if >1 service at same loc at same time
15818  {
15819  if(ServiceAndRepeatNumTotal == "")
15820  {
15821  ServiceAndRepeatNumTotal = Ptr1->ServiceAndRepeatNum + "," + Ptr1->ArrTime;
15822  NumTrainsAtLoc = 1;
15823  }
15824  else
15825  {
15826  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr1->ServiceAndRepeatNum + "," + Ptr1->ArrTime;
15827  }
15828  }
15829  PreviousService = Ptr2->ServiceAndRepeatNum; //last service at relevant time, reset when times differ
15830  if(ServiceAndRepeatNumTotal == "")
15831  {
15832  ServiceAndRepeatNumTotal = Ptr2->ServiceAndRepeatNum + "," + Ptr2->ArrTime;
15833  NumTrainsAtLoc = 1;
15834  }
15835  else
15836  {
15837  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr2->ServiceAndRepeatNum + "," + Ptr2->ArrTime;
15838  }
15839  Ptr1 = Ptr2;
15840  Ptr2++;
15841  if((Ptr2 == LocServiceTimesVector.end()) || (Ptr2->Location != Ptr1->Location) || (!WithinTimeRange(2, BasicTime, Ptr2->ArrTime, ArrRange)))
15842  {
15843  int MaxNumberOfSameDirections = 0;
15844  ServiceAndRepeatNumTotalOutput = ConsolidateSARNTArrDep(1, ServiceAndRepeatNumTotal, NumTrainsAtLoc, Ptr1->Location, true, AnalysisError, MaxNumberOfSameDirections); //sort into alphabetical order and remove duplicates
15845  if(AnalysisError) //has to be Ptr1->Location as Ptr2 loc may have changed
15846  {
15847 // ShowMessage("Error in arrival analysis - file will be incomplete and/or corrupt. Please send railway and timetable files to railwayfeedback@gmail.com for investigation - thanks. Details: " + ServiceAndRepeatNumTotalOutput);
15848  TTFile3.close();
15849  throw Exception(ServiceAndRepeatNumTotalOutput.c_str());
15850 // Utilities->CallLogPop(2224);
15851 // return false;
15852  }
15853  AnsiString Asterisk = "";
15854  if(MaxNumberOfSameDirections >= NumPlats)
15855  {
15856  Asterisk = "* ";
15857  }
15858  //print out a single line for number of trains at loc with all service refs
15859  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << ServiceAndRepeatNumTotalOutput << '\n'; //no description as >1 service
15860  ArrivalsPrinted = true;
15861  ServiceAndRepeatNumTotal = "";
15862  }
15863  if(Ptr2 == LocServiceTimesVector.end())
15864  {
15865  break;
15866  }
15867  if(Ptr2->Location != Ptr1->Location)
15868  {
15869  break;
15870  }
15871  }
15872  if(Ptr2 == LocServiceTimesVector.end())
15873  {
15874  break;
15875  }
15876  }
15877  }
15878  if(!ArrivalsPrinted)
15879  {
15880  TTFile3 << "Nothing to report for arrivals";
15881  }
15882  TTFile3 << "\n\n\n";
15883  }
15884  //end of routine for arrivals
15885 
15886  //departures
15887  if(DepChecked)
15888  {
15889  //sort in DepTime order for each location
15890  Ptr1 = LocServiceTimesVector.begin();
15891  Ptr2 = Ptr1 + 1;
15892  while(Ptr2 != LocServiceTimesVector.end())
15893  {
15894  while(Ptr2->Location == Ptr1->Location) //ends with Ptr2 one past same Location value as Ptr1
15895  {
15896  Ptr2++;
15897  if(Ptr2 == LocServiceTimesVector.end())
15898  {
15899  break;
15900  }
15901  }
15902  std::sort(Ptr1, Ptr2, &LocServiceTimesDepTimeSort);
15903  Ptr1 = Ptr2; //first entry with next name
15904  if(Ptr2 != LocServiceTimesVector.end())
15905  {
15906  Ptr2++;
15907  }
15908  }
15909 
15910  //routine for departures - number of trains departing within the specified range with services listed at the end
15911  TTFile3 << "Departure analysis: an asterisk means that the number of same exit code departures is equal to or greater than the number of platforms.\n";
15912  TTFile3 << "If the total number of departures exceeds the number of platforms the 'Trains present at location analysis' will show an asterisk.\n\n";
15913  MinuteString = " minutes";
15914  AnsiString ServiceAndRepeatNumTotal = "", ServiceAndRepeatNumTotalOutput = "";
15915  if(DepRange == 1)
15916  {
15917  MinuteString = " minute";
15918  }
15919  TTFile3 << "Location,Number of,Number of,Services departing within " << AnsiString(DepRange) << MinuteString << " with their departure times and exit codes\n";
15920  TTFile3 << ",Platforms,Trains\n\n";
15921 
15922  Ptr1 = LocServiceTimesVector.begin();
15923  Ptr2 = Ptr1 + 1;
15924  while(Ptr2 != LocServiceTimesVector.end())
15925  {
15926  PreviousService = "";
15927  NumTrainsAtLoc = 0;
15928  ServiceAndRepeatNumTotal = "";
15929  NumPlats = 0;
15930  NumPlatsAtThisLocCalculated = false;
15931  BasicTime = "";
15932  while((Ptr2->Location != Ptr1->Location) || ((Ptr1->Location == "") && (Ptr2->Location == "")))
15933  {
15934  PreviousService = "";
15935  NumTrainsAtLoc = 0;
15936  ServiceAndRepeatNumTotal = "";
15937  NumPlats = 0;
15938  NumPlatsAtThisLocCalculated = false;
15939  BasicTime = "";
15940  Ptr1++;
15941  Ptr2++;
15942  if(Ptr2 == LocServiceTimesVector.end())
15943  {
15944  break;
15945  }
15946  }
15947  if(Ptr2 == LocServiceTimesVector.end())
15948  {
15949  break;
15950  }
15951  while(Ptr2->Location == Ptr1->Location)
15952  {
15953  PreviousService = "";
15954  NumTrainsAtLoc = 0;
15955  ServiceAndRepeatNumTotal = "";
15956  BasicTime = Ptr1->DepTime; //used to compare later times - later pointer contents have same or later times as sorted in time order
15957  if((Ptr1->Location == "") && (Ptr2->Location == ""))
15958  {
15959  break;
15960  }
15961  while(!WithinTimeRange(3, BasicTime, Ptr2->DepTime, DepRange) || ((Ptr1->DepTime == "") && (Ptr2->DepTime == "")))
15962  {
15963  BasicTime = Ptr2->DepTime; //used to compare later times or last can exceed first
15964  Ptr1++;
15965  Ptr2++;
15966  if(Ptr2 == LocServiceTimesVector.end())
15967  {
15968  break;
15969  }
15970  if(Ptr2->Location != Ptr1->Location)
15971  {
15972  break;
15973  }
15974  }
15975  if(Ptr2 == LocServiceTimesVector.end())
15976  {
15977  break;
15978  }
15979  if(Ptr2->Location != Ptr1->Location)
15980  {
15981  break;
15982  }
15983  while(WithinTimeRange(4, BasicTime, Ptr2->DepTime, DepRange))
15984  {
15985  if((Ptr1->DepTime == "") && (Ptr2->DepTime == ""))
15986  {
15987  break;
15988  }
15989  if(!NumPlatsAtThisLocCalculated) //num plats at relevant location, reset when locations change
15990  {
15991  NumPlats = Track->NumberOfPlatforms(1, Ptr1->Location);
15992  NumPlatsAtThisLocCalculated = true;
15993  }
15994  if(Ptr1->ServiceAndRepeatNum != PreviousService) //don't print it twice if same as last - as will be if >1 service at same loc at same time
15995  {
15996  if(ServiceAndRepeatNumTotal == "")
15997  {
15998  ServiceAndRepeatNumTotal = Ptr1->ServiceAndRepeatNum + "," + Ptr1->DepTime;
15999  NumTrainsAtLoc = 1;
16000  }
16001  else
16002  {
16003  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr1->ServiceAndRepeatNum + "," + Ptr1->DepTime;
16004  }
16005  }
16006  PreviousService = Ptr2->ServiceAndRepeatNum; //last service at relevant time, reset when times differ
16007  if(ServiceAndRepeatNumTotal == "")
16008  {
16009  ServiceAndRepeatNumTotal = Ptr2->ServiceAndRepeatNum + "," + Ptr2->DepTime;
16010  NumTrainsAtLoc = 1;
16011  }
16012  else
16013  {
16014  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr2->ServiceAndRepeatNum + "," + Ptr2->DepTime;
16015  }
16016  Ptr1 = Ptr2;
16017  Ptr2++;
16018  if((Ptr2 == LocServiceTimesVector.end()) || (Ptr2->Location != Ptr1->Location) || (!WithinTimeRange(5, BasicTime, Ptr2->DepTime, DepRange)))
16019  {
16020  int MaxNumberOfSameDirections = 0;
16021  ServiceAndRepeatNumTotalOutput = ConsolidateSARNTArrDep(3, ServiceAndRepeatNumTotal, NumTrainsAtLoc, Ptr1->Location, false, AnalysisError, MaxNumberOfSameDirections); //sort into alphabetical order and remove duplicates
16022  if(AnalysisError) //has to be Ptr1->Location as Ptr2 loc may have changed
16023  {
16024 // ShowMessage("Error in departure analysis - file will be incomplete and/or corrupt. Please send railway and timetable files to railwayfeedback@gmail.com for investigation - thanks. Details: " + ServiceAndRepeatNumTotalOutput);
16025  TTFile3.close();
16026  throw Exception(ServiceAndRepeatNumTotalOutput.c_str());
16027 // Utilities->CallLogPop(2225);
16028 // return false;
16029  }
16030  AnsiString Asterisk = "";
16031  if(MaxNumberOfSameDirections >= NumPlats)
16032  {
16033  Asterisk = "* ";
16034  }
16035  //print out a single line for number of trains at loc with all service refs
16036  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << ServiceAndRepeatNumTotalOutput << '\n'; //no description as >1 service
16037  DeparturesPrinted = true;
16038  ServiceAndRepeatNumTotal = "";
16039  }
16040  if(Ptr2 == LocServiceTimesVector.end())
16041  {
16042  break;
16043  }
16044  if(Ptr2->Location != Ptr1->Location)
16045  {
16046  break;
16047  }
16048  }
16049  if(Ptr2 == LocServiceTimesVector.end())
16050  {
16051  break;
16052  }
16053  }
16054  }
16055  if(!DeparturesPrinted)
16056  {
16057  TTFile3 << "Nothing to report for departures";
16058  }
16059  TTFile3 << "\n\n\n";
16060  }
16061  //end of routine for departures
16062 
16063 
16064  //list trains at locations at same time
16065 
16066  if(AtLocChecked)
16067  {
16068  //sort in AtLocTime order for each location
16069  Ptr1 = LocServiceTimesVector.begin();
16070  Ptr2 = Ptr1 + 1;
16071  while(Ptr2 != LocServiceTimesVector.end())
16072  {
16073  while(Ptr2->Location == Ptr1->Location) //ends with Ptr2 one past same Location value as Ptr1
16074  {
16075  Ptr2++;
16076  if(Ptr2 == LocServiceTimesVector.end())
16077  {
16078  break;
16079  }
16080  }
16081  std::sort(Ptr1, Ptr2, &LocServiceTimesAtLocTimeSort);
16082  Ptr1 = Ptr2; //first entry with next name
16083  if(Ptr2 != LocServiceTimesVector.end())
16084  {
16085  Ptr2++;
16086  }
16087  }
16088 
16089  //print out simultaneous AtLocs (don't need range of times for AtLocs)
16090  TTFile3 << "Trains present at location analysis: an asterisk means that the number of trains at the location is greater than the number of platforms.\n\n";
16091  TTFile3 << "Location,Number of,Number of,Time,Services at the location at that time\n";
16092  TTFile3 << ",Platforms,Trains,\n\n";
16093  AnsiString ServiceAndRepeatNumTotal = "", ServiceAndRepeatNumTotalOutput = "";
16094  Ptr1 = LocServiceTimesVector.begin();
16095  Ptr2 = Ptr1 + 1;
16096  while(Ptr2 != LocServiceTimesVector.end())
16097  {
16098  PreviousService = "";
16099  ServiceAndRepeatNumTotal = "";
16100  NumTrainsAtLoc = 0;
16101  NumPlats = 0;
16102  NumPlatsAtThisLocCalculated = false;
16103  FrhCount = 0;
16104  while((Ptr2->Location != Ptr1->Location) || ((Ptr1->Location == "") && (Ptr2->Location == "")))
16105  {
16106  PreviousService = "";
16107  ServiceAndRepeatNumTotal = "";
16108  NumTrainsAtLoc = 0;
16109  NumPlats = 0;
16110  NumPlatsAtThisLocCalculated = false;
16111  FrhCount = 0;
16112  Ptr1++;
16113  Ptr2++;
16114  if(Ptr2 == LocServiceTimesVector.end())
16115  {
16116  break;
16117  }
16118  }
16119  if(Ptr2 == LocServiceTimesVector.end())
16120  {
16121  break;
16122  }
16123  while(Ptr2->Location == Ptr1->Location)
16124  {
16125  if(Ptr1->FrhMarker == "Frh") //this test is made here and each time Ptr1 increases with Ptr1 & 2 at same loc so as to catch them all
16126  {
16127  FrhCount++;
16128  Ptr1->FrhMarker = "FrhCounted"; //to avoid double counting
16129  }
16130  PreviousService = "";
16131  NumTrainsAtLoc = 0;
16132  ServiceAndRepeatNumTotal = "";
16133  if((Ptr1->Location == "") && (Ptr2->Location == ""))
16134  {
16135  break;
16136  }
16137  while((Ptr2->AtLocTime != Ptr1->AtLocTime) || ((Ptr1->AtLocTime == "") && (Ptr2->AtLocTime == "")))
16138  {
16139  Ptr1++;
16140  if(Ptr1->FrhMarker == "Frh")
16141  {
16142  FrhCount++;
16143  Ptr1->FrhMarker = "FrhCounted"; //to avoid double counting
16144  }
16145  Ptr2++;
16146  if(Ptr2 == LocServiceTimesVector.end())
16147  {
16148  break;
16149  }
16150  if(Ptr2->Location != Ptr1->Location)
16151  {
16152  break;
16153  }
16154  }
16155  if(Ptr2 == LocServiceTimesVector.end())
16156  {
16157  break;
16158  }
16159  if(Ptr2->Location != Ptr1->Location)
16160  {
16161  break;
16162  }
16163  while(Ptr2->AtLocTime == Ptr1->AtLocTime)
16164  {
16165  if((Ptr1->AtLocTime == "") && (Ptr2->AtLocTime == ""))
16166  {
16167  break;
16168  }
16169  if(!NumPlatsAtThisLocCalculated) //num plats at relevant location, reset when locations change
16170  {
16171  NumPlats = Track->NumberOfPlatforms(2, Ptr1->Location);
16172  NumPlatsAtThisLocCalculated = true;
16173  }
16174  if(Ptr1->ServiceAndRepeatNum != PreviousService) //don't print it twice if same as last - as will be if >1 service at same loc at same time
16175  {
16176  if(ServiceAndRepeatNumTotal == "")
16177  {
16178  ServiceAndRepeatNumTotal = Ptr1->ServiceAndRepeatNum;
16179  NumTrainsAtLoc = 1;
16180  }
16181  else
16182  {
16183  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr1->ServiceAndRepeatNum;
16184  }
16185  }
16186  PreviousService = Ptr2->ServiceAndRepeatNum; //last service at relevant time, reset when times differ, has to be Ptr2 to compare Ptr1 at next round when incremented
16187  if(ServiceAndRepeatNumTotal == "")
16188  {
16189  ServiceAndRepeatNumTotal = Ptr2->ServiceAndRepeatNum;
16190  NumTrainsAtLoc = 1;
16191  }
16192  else
16193  {
16194  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr2->ServiceAndRepeatNum;
16195  }
16196  Ptr1 = Ptr2;
16197  if(Ptr1->FrhMarker == "Frh")
16198  {
16199  FrhCount++;
16200  Ptr1->FrhMarker = "FrhCounted"; //to avoid double counting
16201  }
16202  Ptr2++;
16203  if((Ptr2 == LocServiceTimesVector.end()) || (Ptr2->Location != Ptr1->Location) || (Ptr2->AtLocTime != Ptr1->AtLocTime))
16204  {
16205 //old text //only print out if no remainers (1st condition), change in remainers (2nd condition) or change in ServiceAndRepeatNumTotalOutput, and >1 train (later condition)
16206 //new text //don't print out if all remainers or if only 1 train at loc
16207  ServiceAndRepeatNumTotalOutput = ConsolidateSARNTAtLoc(1, ServiceAndRepeatNumTotal, NumTrainsAtLoc); //sort into alphabetical order, remove duplicates, and calculate new value for NumTrainsAtLoc
16208 //old condits if((FrhCount == 0) || (FrhCount != LastFrhCount) || (PreviousServiceAndRepeatNumTotalOutput != ServiceAndRepeatNumTotalOutput))//don't print if same output
16209 /*new condits*/ if((NumTrainsAtLoc > 1) && ((FrhCount < NumTrainsAtLoc) || (FrhCount != LastFrhCount)))
16210  {
16211  AnsiString Asterisk = "";
16212  if(NumTrainsAtLoc > NumPlats)
16213  {
16214  Asterisk = "* ";
16215  }
16216  //print out a single line for number of trains at loc with all service refs
16217  if(FrhCount == 0)
16218  {
16219  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << Ptr1->AtLocTime << "," << ServiceAndRepeatNumTotalOutput << '\n';
16220  }
16221  else if(FrhCount == 1)
16222  {
16223  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << Ptr1->AtLocTime << " (1 remains here)," << ServiceAndRepeatNumTotalOutput << '\n';
16224  }
16225  else
16226  {
16227  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << Ptr1->AtLocTime << " (" << FrhCount << " remain here)," << ServiceAndRepeatNumTotalOutput << '\n';
16228  }
16229  LastFrhCount = FrhCount;
16230  PreviousServiceAndRepeatNumTotalOutput = ServiceAndRepeatNumTotalOutput;
16231  AtLocsPrinted = true;
16232  ServiceAndRepeatNumTotal = "";
16233  }
16234  }
16235  if(Ptr2 == LocServiceTimesVector.end())
16236  {
16237  break;
16238  }
16239  if(Ptr2->Location != Ptr1->Location)
16240  {
16241  break;
16242  }
16243  }
16244  if(Ptr2 == LocServiceTimesVector.end())
16245  {
16246  break;
16247  }
16248  }
16249  }
16250  if(!AtLocsPrinted)
16251  {
16252  TTFile3 << "Nothing to report for trains at locations";
16253  }
16254  TTFile3 << "\n\n\n";
16255  //end of simultaneous AtLocs
16256 
16257 /*
16258  //print out the full vector here for testing purposes
16259  TTFile3 << "Full LocServiceTimesVector\n\n";
16260  TTFile3 << "Location,AtLocTime,ArrTime,DepTime,ServiceAndRepeatNum,Description\n\n";
16261 
16262  for(TLocServiceTimesVector::iterator Ptr = LocServiceTimesVector.begin(); Ptr != LocServiceTimesVector.end(); Ptr++)
16263  {
16264  TTFile3 << Ptr->Location << "," << Ptr->AtLocTime << "," << Ptr->ArrTime << "," << Ptr->DepTime << "," << Ptr->ServiceAndRepeatNum << "," << Ptr->FrhMarker << '\n';
16265  }
16266 
16267  TTFile3 << "\n\n\n";
16268 */
16269  }
16270 
16271  TTFile3.close();
16272  Utilities->CallLogPop(2212);
16273  return true;
16274  }
16275 
16276  catch(const Exception &e)
16277  {
16278  AnsiString TTErrorFileName = "Analysis Error.txt";
16279  TTErrorFileName = CurDir + "\\Formatted timetables\\" + TTErrorFileName;
16280  std::ofstream TTError(TTErrorFileName.c_str());
16281  if(TTError == 0)
16282  {
16283  ShowMessage("Analysis error file failed to open - can't be created");
16284  Utilities->CallLogPop(2233);
16285  return false;
16286  }
16287  AnsiString TimeNow = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
16288  TTError << TimeNow.c_str() << "\n" << ArrRange << "\n" << ArrChecked << "\n" << DepRange << "\n" <<
16289  DepChecked << "\n" << AtLocChecked << "\n" << AnsiString(e.Message);
16290  TTError.close();
16291  ShowMessage("Error in Conflict Analysis: A file called 'Analysis Error.txt' has been created in your Formatted timetables folder. Please send this file together with your railway and timetable files to railwayfeedback@gmail.com for investigation - many thanks");
16292  Utilities->CallLogPop(2226);
16293  return false;
16294  }
16295 }
16296 
16297 // ---------------------------------------------------------------------------
16298 
16299 bool TTrainController::WithinTimeRange(int Caller, AnsiString Time1, AnsiString Time2, int MinuteRange) //times are "HH:MM"
16300 {
16301 //convert times to integer minutes
16302  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",WithinTimeRange," + Time1 + "," + Time2 + "," + AnsiString(MinuteRange));
16303  if((Time1 == "") || (Time2 == ""))
16304  {
16305  Utilities->CallLogPop(2213);
16306  return false;
16307  }
16308  int Mins = Time1.SubString(4,2).ToInt();
16309  int Hours = Time1.SubString(1,2).ToInt();
16310  int Time1Mins = (Hours * 60) + Mins;
16311  Mins = Time2.SubString(4,2).ToInt();
16312  Hours = Time2.SubString(1,2).ToInt();
16313  int Time2Mins = (Hours * 60) + Mins;
16314  if(abs(Time1Mins - Time2Mins) <= MinuteRange)
16315  {
16316  Utilities->CallLogPop(2214);
16317  return true;
16318  }
16319  Utilities->CallLogPop(2215);
16320  return false;
16321 }
16322 
16323 // ---------------------------------------------------------------------------
16324 
16325 AnsiString TTrainController::ConsolidateSARNTArrDep(int Caller, const AnsiString Input, int &NumTrainsAtLoc, AnsiString Location, bool Arrival,
16326  bool &AnalysisError, int &MaxNumberOfSameDirections)
16327 {//input consists of services and service Arr or Dep times as a comma separated list, Location needed to determine direction information
16328 
16329  try
16330  {
16331  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ConsolidateSARNTArrDep," + Input);
16332  AnsiString Output = "", OneService = "", TempStr1 = "", TempStr2 = "";
16333  int SCPos = 0;
16334  std::list<AnsiString> ServiceList; //this is the list of services with times extracted from Input - not to be confused with ServiceCallingPointsList
16335  //first change every second comma in Input to a semicolon so can separate services but keep times with services
16336  bool EvenComma = false;
16337  for(int x = 1; x <= Input.Length(); x++)
16338  {
16339  TempStr1 = Input[x];
16340  if(TempStr1 == AnsiString(',') && EvenComma)
16341  {
16342  TempStr2 += ';';
16343  }
16344  else
16345  {
16346  TempStr2 += Input[x];
16347  }
16348  if(TempStr1 == AnsiString(','))
16349  {
16350  EvenComma = !EvenComma;
16351  }
16352  }
16353  //load up the list of services with associated times
16354  while(TempStr2.Length() > 0)
16355  {
16356  SCPos = TempStr2.Pos(';');
16357  if(SCPos > 0) //0 if not found, as won't be when only one service left
16358  {
16359  OneService = TempStr2.SubString(1, SCPos - 1);
16360  ServiceList.push_back(OneService);
16361  TempStr2 = TempStr2.SubString(SCPos + 1, TempStr2.Length() - SCPos);
16362  }
16363  else //no semicolon so looking at last (or only) element
16364  {
16365  ServiceList.push_back(TempStr2);
16366  TempStr2 = "";
16367  }
16368  }
16369  ServiceList.sort(); // alphabetical order
16370  ServiceList.unique(); //remove duplicates
16371  NumTrainsAtLoc = ServiceList.size(); //calc this after removing duplicates as may not have changed
16372 
16373  //now add direction information from AllServiceCallingLocsMap - key is service ref and value a list of calling points in order
16374  int DirectionMarker = 0; //this is added in & runs from 1 upwards, same marker for diff services = same direction
16375  //first add the direction marker "&0" for not yet allocated - '&' is an identifier
16376  std::list<AnsiString>::iterator SLIt, SLIt1, SLIt2, SLIt3;
16377 
16378  for(SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
16379  {
16380  *SLIt = *SLIt + "&0"; //add in a basic direction marker to each service
16381  }
16382  SLIt3 = ServiceList.end();
16383  SLIt3--; //so points to last element
16384  AnsiString ServiceRef1, ServiceRef2, AnsiTime1, AnsiTime2, RepeatInfo1, RepeatInfo2; //1 refers to first for..next loop & 2 to second
16385  int AmpersandPos, SpacePos, CommaPos1, CommaPos2, RepeatNum1, RepeatNum2;
16386  TAllServiceCallingLocsMap::iterator ASCLIt1, ASCLIt2;
16387  TServiceCallingLocsList ServiceCallingLocsList1, ServiceCallingLocsList2;
16388  MaxNumberOfSameDirections = 0; //at end of each SLIt loop if SameDirectionCount > MaxNumberOfSameDirections then MaxNumberOfSameDirections = SameDirectionCount
16389  int SameDirectionCount = 0; //starts at 1 at each SLIt loop (because SLIt1 entry already has a DirectionMarker) and increments for every same direction
16390 
16391  for(std::list<AnsiString>::iterator SLIt1 = ServiceList.begin(); SLIt1 != SLIt3; SLIt1++) //should be end() - 1 but can't use -1 with lists so have to improvise
16392  {
16393  SLIt = SLIt1;
16394  SLIt++; //so points to one after SLIt1
16395  if(SLIt1->SubString(SLIt1->Length() - 1, 2) != AnsiString("&0"))
16396  {
16397  continue; //already allocated so skip to the next
16398  }
16399  else
16400  {
16401  CommaPos1 = SLIt1->Pos(','); //can't be 0
16402  ServiceRef1 = SLIt1->SubString(1, CommaPos1 - 1);
16403  //but this contains "(First service..." etc so need to strip these, but use to extract RepeatNum
16404  SpacePos = ServiceRef1.Pos(' ');
16405  RepeatNum1 = 0;
16406  if(SpacePos > 0) //otherwise it's already correct
16407  {
16408  RepeatInfo1 = ServiceRef1.SubString(SpacePos + 2, ServiceRef1.Length() - SpacePos - 2); //drops the brackets and leaves "First service", "Repeat 2" etc
16409  ServiceRef1 = ServiceRef1.SubString(1, SpacePos - 1);
16410  if(RepeatInfo1[1] == 'F')
16411  {
16412  RepeatNum1 = 0;
16413  }
16414  else
16415  {
16416  SpacePos = RepeatInfo1.Pos(' ');
16417  RepeatNum1 = RepeatInfo1.SubString(SpacePos + 1, RepeatInfo1.Length() - SpacePos).ToInt();
16418  }
16419  }
16420  AnsiTime1 = SLIt1->SubString(CommaPos1 + 1, SLIt1->Length() - CommaPos1);
16421  //but this includes the "&0" etc so need to strip these
16422  AmpersandPos = AnsiTime1.Pos('&');
16423  AnsiTime1 = AnsiTime1.SubString(1, AmpersandPos - 1);
16424 
16425  ASCLIt1 = AllServiceCallingLocsMap.find(ServiceRef1);
16426  if(ASCLIt1 == AllServiceCallingLocsMap.end()) //can't find it
16427  {
16428  throw Exception("ASCLIt1 Error in " + Input);
16429  }
16430  ServiceCallingLocsList1 = ASCLIt1->second;
16431  AmpersandPos = SLIt1->Pos('&');
16432  *SLIt1 = SLIt1->SubString(1, AmpersandPos); //truncate up to & leave in the '&'
16433  *SLIt1 = *SLIt1 + AnsiString(++DirectionMarker); //now add the next marker (pre-increment), allow for it being more than one digit
16434 
16435  SameDirectionCount = 1;
16436  for(SLIt2 = SLIt; SLIt2 != ServiceList.end(); SLIt2++)
16437  {
16438  CommaPos2 = SLIt2->Pos(','); //can't be 0
16439  ServiceRef2 = SLIt2->SubString(1, CommaPos2 - 1);
16440  //but this contains "(First service..." etc so need to strip these
16441  SpacePos = ServiceRef2.Pos(' ');
16442  RepeatNum2 = 0;
16443  if(SpacePos > 0) //otherwise it's already correct
16444  {
16445  RepeatInfo2 = ServiceRef2.SubString(SpacePos + 2, ServiceRef2.Length() - SpacePos - 2); //drops the brackets and leaves "First service", "Repeat 2" etc
16446  ServiceRef2 = ServiceRef2.SubString(1, SpacePos - 1);
16447  if(RepeatInfo2[1] == 'F')
16448  {
16449  RepeatNum2 = 0;
16450  }
16451  else
16452  {
16453  SpacePos = RepeatInfo2.Pos(' ');
16454  RepeatNum2 = RepeatInfo2.SubString(SpacePos + 1, RepeatInfo2.Length() - SpacePos).ToInt();
16455  }
16456 
16457  }
16458  AnsiTime2 = SLIt2->SubString(CommaPos2 + 1, SLIt2->Length() - CommaPos2);
16459  //but this includes the "&0" etc so need to strip these
16460  AmpersandPos = AnsiTime2.Pos('&');
16461  AnsiTime2 = AnsiTime2.SubString(1, AmpersandPos - 1);
16462 
16463  ASCLIt2 = AllServiceCallingLocsMap.find(ServiceRef2);
16464  if(ASCLIt2 == AllServiceCallingLocsMap.end()) //can't find it
16465  {
16466  throw Exception("ASCLIt2 Error in " + Input);
16467  }
16468  ServiceCallingLocsList2 = ASCLIt2->second;
16469  //now compare the two
16470  if(SameDirection(0, ServiceRef1, ServiceRef2, AnsiTime1, AnsiTime2, RepeatNum1, RepeatNum2, ServiceCallingLocsList1, ServiceCallingLocsList2, Location, Arrival))
16471  {
16472  int AmpersandPos = SLIt2->Pos('&');
16473  *SLIt2 = SLIt2->SubString(1, AmpersandPos); //truncate up to & leave in the '&'
16474  *SLIt2 = *SLIt2 + AnsiString(DirectionMarker); //now add the same marker as *SLIt1
16475  SameDirectionCount++;
16476  }
16477  }
16478  if(SameDirectionCount > MaxNumberOfSameDirections)
16479  {
16480  MaxNumberOfSameDirections = SameDirectionCount;
16481  }
16482  }
16483  }
16484 
16485  if(SLIt3->SubString(SLIt3->Length() - 1, 2) == AnsiString("&0"))//*SLTIt3 is the last in the list and may not have been allocated, if not it doesn't match
16486  { //any existing direction so allocate it now
16487  AmpersandPos = SLIt3->Pos('&');
16488  *SLIt3 = SLIt3->SubString(1, AmpersandPos); //truncate up to & leave in the '&'
16489  *SLIt3 = *SLIt3 + AnsiString(++DirectionMarker);
16490  }
16491 
16492  //now change direction markers to upper case letters beginning with 'A' (and continuing with 'AA' if exceed 26) & add a comma before so have ServiceRef, DirectionMarker, Time
16493  for(SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
16494  {
16495  //extract the DirectionMarker as an integer
16496  AmpersandPos = SLIt->Pos('&');
16497  AnsiString DirectionMarkerString = SLIt->SubString(AmpersandPos + 1, SLIt->Length() - AmpersandPos); //extract the number as an ansistring
16498  AnsiString ServiceWithoutMarker = SLIt->SubString(1, AmpersandPos - 1); //truncate the &number
16499  DirectionMarker = DirectionMarkerString.ToInt();
16500  AnsiString DirectionSuffix = "";
16501  char c;
16502  if(DirectionMarker < 27)
16503  {
16504  c = 64 + DirectionMarker; //so 1 -> 'A'
16505  DirectionSuffix = "," + AnsiString(c);
16506  }
16507  else if(DirectionMarker < 53)
16508  {
16509  c = 65 + DirectionMarker - 27; //so 27 -> 'AA'
16510  DirectionSuffix = ",A" + AnsiString(c);
16511  }
16512  else
16513  {
16514  DirectionSuffix = ",?"; //shouldn'tn ever get this far!
16515  }
16516  *SLIt = ServiceWithoutMarker + DirectionSuffix;
16517  }
16518  //now prepare the final consolidated output
16519  for(SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
16520  {
16521  Output = Output + *SLIt + ","; //will end up with an unwanted comma at the end
16522  }
16523  if(Output.Length() > 0)
16524  {
16525  Output = Output.SubString(1, Output.Length() - 1); //remove the last comma
16526  }
16527  Utilities->CallLogPop(2216);
16528  return Output;
16529  }
16530 
16531  catch(const Exception &e)
16532  {
16533  AnalysisError = true;
16534  Utilities->CallLogPop(2227);
16535  return e.Message;
16536  }
16537 }
16538 
16539 // ---------------------------------------------------------------------------
16540 
16541 AnsiString TTrainController::ConsolidateSARNTAtLoc(int Caller, const AnsiString Input, int &NumTrainsAtLoc)
16542 {//similar to above but doesn't include times in the input
16543  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ConsolidateSARNTAtLoc," + Input);
16544  AnsiString InternalInput = Input, Output = "", OneService = "";
16545  int CommaPos = 0;
16546  std::list<AnsiString> ServiceList;
16547  //load up the list
16548  while(InternalInput.Length() > 0)
16549  {
16550  CommaPos = InternalInput.Pos(',');
16551  if(CommaPos > 0) //0 if not found, as won't be when only one service left
16552  {
16553  OneService = InternalInput.SubString(1, CommaPos - 1);
16554  ServiceList.push_back(OneService);
16555  InternalInput = InternalInput.SubString(CommaPos + 1, InternalInput.Length() - CommaPos);
16556  }
16557  else //no comma so looking at last (or only) element
16558  {
16559  ServiceList.push_back(InternalInput);
16560  InternalInput = "";
16561  }
16562  }
16563 
16564  ServiceList.sort(); // alphabetical order
16565  ServiceList.unique(); //remove duplicates
16566  NumTrainsAtLoc = ServiceList.size(); //calc this after removing duplicates as may not have changed
16567  for(std::list<AnsiString>::iterator SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
16568  {
16569  Output = Output + *SLIt + ","; //will end up with an unwanted comma at the end
16570  }
16571  if(Output.Length() > 0)
16572  {
16573  Output = Output.SubString(1, Output.Length() - 1); //remove the last comma
16574  }
16575  Utilities->CallLogPop(2217);
16576  return Output;
16577 }
16578 
16579 // ---------------------------------------------------------------------------
16580 
16581 
16582 bool TTrainController::SameDirection(int Caller, AnsiString Ref1In, AnsiString Ref2In, AnsiString Time1, AnsiString Time2, int RepeatNum1, int RepeatNum2, TServiceCallingLocsList List1,
16583  TServiceCallingLocsList List2, AnsiString Location, bool Arrival)
16584  {
16585  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SameDirection," + Ref1In + "," + Ref2In + "," + Time1 + "," + Time2 + "," +
16586  AnsiString(RepeatNum1) + "," + AnsiString(RepeatNum2) + "," + Location);
16587 
16588  std::list<AnsiString>::iterator LP1 = 0, LP2 = 0, ListPtr1 = 0, ListPtr2 = 0, LocPtr1 = 0, LocPtr2 = 0; //LP1 & 2 are temporary pointers, ListPtrs are
16589  //general list pointers, LocPtrs point to Location in the two lists
16590 
16591  //first find the relevant values for LocPtr1 & LocPtr2 taking account of cdts and times
16592  //for List1
16593  bool LocFound = false;
16594  AnsiString Ref1 = Ref1In, Ref2 = Ref2In;
16595  int IncMinutes;
16596  TDateTime FirstServiceTime;
16597 
16598  //first need to strip off /1, /2 etc if present from Ref1 & Ref2 (leave Ref1In & Ref2In for error message & retain value as target in finding the correct reference for cdts)
16599  int Ref1Target = 0, Ref1Count = 0;
16600  int SlashPos = Ref1.Pos('/');
16601  if(SlashPos > 0) //if 0 Ref1 == Ref1In & target stays at 0
16602  {
16603  Ref1Target = Ref1.SubString(SlashPos + 1, Ref1.Length() - SlashPos).ToInt();
16604  Ref1 = Ref1.SubString(1, SlashPos - 1); //truncate up to but omit '/'
16605  }
16606  int Ref2Target = 0, Ref2Count = 0;
16607  SlashPos = Ref2.Pos('/');
16608  if(SlashPos > 0) //if 0 leave as is
16609  {
16610  Ref2Target = Ref2.SubString(SlashPos + 1, Ref2.Length() - SlashPos).ToInt();
16611  Ref2 = Ref2.SubString(1, SlashPos - 1); //truncate up to but omit '/'
16612  }
16613 
16614  for(ListPtr1 = List1.begin(); ListPtr1 != List1.end(); ListPtr1++) //note that when this routine entered Ref1In & Ref2In are already set to the correct services,
16615  { //even if others have same names. But if there are cdt's then need to refind the correct service
16616  if((*ListPtr1) == Location) //
16617  {
16618  LocPtr1 = ListPtr1; //may be modified later
16619  LocFound = true;
16620  }
16621  if(ListPtr1->SubString(1, 3) == "%%%")
16622  {
16623  AnsiString CDTTime = ListPtr1->SubString(4, 5);
16624  //now adjust the time to correspond to the repeat if there is one
16625  if(RepeatNum1 > 0) //if it is 0 then AnsiTime1 is already valid
16626  {
16627  IncMinutes = -1;
16628  FirstServiceTime = TDateTime(-1);
16629  bool BreakFlag = false;
16630  for(TTrainDataVector::iterator TDVIt = TrainDataVector.begin(); TDVIt != TrainDataVector.end(); TDVIt++)
16631  {
16632  if(TDVIt->ServiceReference == Ref1)
16633  {
16634  if(Ref1Target > Ref1Count)
16635  {
16636  Ref1Count++;
16637  continue;
16638  }
16639  IncMinutes = TDVIt->ActionVector.back().RearStartOrRepeatMins;
16640  for(TActionVector::iterator AVIt = TDVIt->ActionVector.begin(); AVIt != TDVIt->ActionVector.end(); AVIt++)
16641  {
16642  if(Utilities->Format96HHMM(AVIt->EventTime) == CDTTime)
16643  {
16644  FirstServiceTime = AVIt->EventTime; //i.e. the FirstService value of CDTTime
16645  BreakFlag = true;
16646  break;
16647  }
16648  if(Utilities->Format96HHMM(AVIt->ArrivalTime) == CDTTime) //add arr & dep in case find sooner (though dep shouldn't be sooner)
16649  {
16650  FirstServiceTime = AVIt->ArrivalTime;
16651  BreakFlag = true;
16652  break;
16653  }
16654  if(Utilities->Format96HHMM(AVIt->DepartureTime) == CDTTime)
16655  {
16656  FirstServiceTime = AVIt->DepartureTime;
16657  BreakFlag = true;
16658  break;
16659  }
16660  }
16661  if(BreakFlag)
16662  {
16663  break;
16664  }
16665  }
16666  }
16667  if(IncMinutes == -1)
16668  {
16669  throw Exception("Failed to find service for ServiceRef1 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
16670  }
16671  if(FirstServiceTime == TDateTime(-1))
16672  {
16673  throw Exception("Failed to find first service time for ServiceRef1 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
16674  }
16675  CDTTime = Utilities->Format96HHMM(TrainController->GetRepeatTime(60, FirstServiceTime, RepeatNum1, IncMinutes));
16676  }
16677  if(!Arrival && (Time1 == CDTTime)) //continue if equal in case next is a departure for the Location
16678  {
16679  LocFound = false;
16680  continue;
16681  }
16682  if(Arrival && (Time1 == CDTTime)) //gone far enough so can stop
16683  {
16684  break;
16685  }
16686  if(Time1 > CDTTime) //not there yet so go on
16687  {
16688  LocFound = false;
16689  continue;
16690  }
16691  if(Time1 < CDTTime) //gone too far so can stop now
16692  {
16693  break;
16694  }
16695  }
16696  }
16697  if(!LocFound) //have to find it in both lists
16698  {
16699  Utilities->CallLogPop(2228);
16700  return false;
16701  }
16702 
16703  //for List2
16704  LocFound = false;
16705  for(ListPtr2 = List2.begin(); ListPtr2 != List2.end(); ListPtr2++)
16706  {
16707  if((*ListPtr2) == Location)
16708  {
16709  LocPtr2 = ListPtr2; //may be modified later
16710  LocFound = true;
16711  }
16712  if(ListPtr2->SubString(1, 3) == "%%%")
16713  {
16714  AnsiString CDTTime = ListPtr2->SubString(4, 5);
16715  //now adjust the time to correspond to the repeat if there is one
16716  if(RepeatNum2 > 0) //if it is 0 then AnsiTime1 is already valid
16717  {
16718  IncMinutes = -1;
16719  FirstServiceTime = TDateTime(-1);
16720  bool BreakFlag = false;
16721  for(TTrainDataVector::iterator TDVIt = TrainDataVector.begin(); TDVIt != TrainDataVector.end(); TDVIt++)
16722  {
16723  if(TDVIt->ServiceReference == Ref2)
16724  {
16725  if(Ref2Target > Ref2Count)
16726  {
16727  Ref2Count++;
16728  continue;
16729  }
16730  IncMinutes = TDVIt->ActionVector.back().RearStartOrRepeatMins;
16731  for(TActionVector::iterator AVIt = TDVIt->ActionVector.begin(); AVIt != TDVIt->ActionVector.end(); AVIt++)
16732  {
16733  if(Utilities->Format96HHMM(AVIt->EventTime) == CDTTime)
16734  {
16735  FirstServiceTime = AVIt->EventTime;
16736  BreakFlag = true;
16737  break;
16738  }
16739  if(Utilities->Format96HHMM(AVIt->ArrivalTime) == CDTTime)
16740  {
16741  FirstServiceTime = AVIt->ArrivalTime;
16742  BreakFlag = true;
16743  break;
16744  }
16745  if(Utilities->Format96HHMM(AVIt->DepartureTime) == CDTTime)
16746  {
16747  FirstServiceTime = AVIt->DepartureTime;
16748  BreakFlag = true;
16749  break;
16750  }
16751  }
16752  if(BreakFlag)
16753  {
16754  break;
16755  }
16756  }
16757  }
16758  if(IncMinutes == -1)
16759  {
16760  throw Exception("IncMinutes -1 for ServiceRef2 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
16761  }
16762  if(FirstServiceTime == TDateTime(-1))
16763  {
16764  throw Exception("First service time -1 for ServiceRef2 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
16765  }
16766  CDTTime = Utilities->Format96HHMM(TrainController->GetRepeatTime(61, FirstServiceTime, RepeatNum2, IncMinutes));
16767  }
16768  if(!Arrival && (Time2 == CDTTime)) //continue if equal in case next is a departure for the Location
16769  {
16770  LocFound = false;
16771  continue;
16772  }
16773  if(Arrival && (Time2 == CDTTime)) //gone far enough so can stop
16774  {
16775  break;
16776  }
16777  if(Time2 > CDTTime) //not there yet so go on
16778  {
16779  LocFound = false;
16780  continue;
16781  }
16782  if(Time2 < CDTTime) //gone too far so can stop now
16783  {
16784  break;
16785  }
16786  }
16787  }
16788  if(!LocFound) //have to find it in both lists, and should be found but allow for it not being
16789  {
16790  Utilities->CallLogPop(2229);
16791  return false;
16792  }
16793 
16794  //now, for the arrival analysis, see if there is a common location before the LocPtrs & within any cdts, and if so return true, else return false
16795  //set ListPtr1 to the search start position
16796  if(Arrival)
16797  {
16798  LP1 = List1.begin();
16799  LP1--; //now points to before the first entry
16800  for(ListPtr1 = LocPtr1; ListPtr1 != LP1; ListPtr1--) //search backwards from Location
16801  {
16802  if(ListPtr1 == List1.begin())
16803  {
16804  break;
16805  }
16806  if(ListPtr1->SubString(1, 3) == "%%%") //a cdt event
16807  {
16808  ListPtr1++; //point to one past the cdt
16809  break;
16810  }
16811  }
16812  //set ListPtr2 to the search start position
16813  LP2 = List2.begin();
16814  LP2--; //now points to before the first entry
16815  for(ListPtr2 = LocPtr2; ListPtr2 != LP2; ListPtr2--)
16816  {
16817  if(ListPtr2 == List2.begin())
16818  {
16819  break;
16820  }
16821  if(ListPtr2->SubString(1, 3) == "%%%") //a cdt event
16822  {
16823  ListPtr2++; //point to one past the cdt
16824  break;
16825  }
16826  }
16827  //ListPtr1 & 2 now at search start position
16828  LP1 = ListPtr1;
16829  LP2 = ListPtr2;
16830  //now search forwards, i.e. for common locations before Location
16831  for(ListPtr1 = LP1; ListPtr1 != List1.end(); ListPtr1++)
16832  {
16833  if(ListPtr1 == LocPtr1) //reached Location without finding a common earlier location so skip to the backwards check
16834  {
16835  break;
16836  }
16837  if(ListPtr1->SubString(1, 3) == "%%%") //reached a cdt event without finding a common earlier location
16838  {
16839  break;
16840  }
16841  for(ListPtr2 = LP2; ListPtr2 != List2.end(); ListPtr2++)
16842  {
16843  if(ListPtr2 == LocPtr2) //not found common earlier location so go to the next ListPtr1
16844  {
16845  break;
16846  }
16847  if(ListPtr2->SubString(1, 3) == "%%%") //reached a cdt event without finding a common earlier location so go to the next ListPtr1
16848  {
16849  break;
16850  }
16851  if((*ListPtr1) == (*ListPtr2)) //found a common earlier location
16852  {
16853  Utilities->CallLogPop(2230);
16854  return true;
16855  }
16856  }
16857  }
16858  }
16859 
16860  //now, for the departure analysis, reset the start positions and search locations after Location
16861 
16862  else
16863  {
16864  LP1 = LocPtr1; LP1++; //start at one past the location itself
16865  LP2 = LocPtr2; LP2++;
16866  for(ListPtr1 = LP1; ListPtr1 != List1.end(); ListPtr1++)
16867  {
16868  if(ListPtr1 == List1.end()) //reached end point so stop
16869  {
16870  break;
16871  }
16872  if(ListPtr1->SubString(1, 3) == "%%%") //reached a cdt event without finding a common location
16873  {
16874  break;
16875  }
16876  for(ListPtr2 = LP2; ListPtr2 != List2.end(); ListPtr2++)
16877  {
16878  if(ListPtr2 == List2.end()) //reached end point so go to next ListPtr1
16879  {
16880  break;
16881  }
16882  if(ListPtr2->SubString(1, 3) == "%%%") //reached a cdt event without finding a common location so go to the next ListPtr1
16883  {
16884  break;
16885  }
16886  if((*ListPtr1) == (*ListPtr2)) //found a common later location
16887  {
16888  Utilities->CallLogPop(2231);
16889  return true;
16890  }
16891  }
16892  }
16893  }
16894  Utilities->CallLogPop(2232);
16895  return false;
16896  }
16897 
16898 // ---------------------------------------------------------------------------
16899 
16900 AnsiString TTrainController::GetExitLocationAndAt(int Caller, TExitList &ExitList) const
16901 {
16902  // check all timetable names in list, if all same return " at [name]" else return ""
16903  if(ExitList.empty())
16904  return "";
16905 
16906  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetExitLocationAndAt");
16907  AnsiString StartName = Track->TrackElementAt(735, *(ExitList.begin())).ActiveTrackElementName;
16908 
16909  if(StartName == "")
16910  {
16911  if(ExitList.size() == 1)
16912  {
16913  AnsiString ID = Track->TrackElementAt(738, *(ExitList.begin())).ElementID;
16914  Utilities->CallLogPop(1571);
16915  return " at " + ID;
16916  }
16917  else
16918  {
16919  Utilities->CallLogPop(1572);
16920  return "";
16921  }
16922  }
16923  for(TExitListIterator ELIT = ExitList.begin(); ELIT != ExitList.end(); ELIT++)
16924  {
16925  if(Track->TrackElementAt(736, *ELIT).ActiveTrackElementName != StartName)
16926  {
16927  Utilities->CallLogPop(1570);
16928  return "";
16929  }
16930  }
16931  Utilities->CallLogPop(1569);
16932  return " at " + StartName;
16933 }
16934 
16935 // ---------------------------------------------------------------------------
16936 /* can't trust this as locations within a vector may not be contiguous
16937  bool TTrainController::IsServiceTerminating(int Caller, TTrainDataEntry *TDEPtr, TActionVectorEntry *AVPtr)
16938  {
16939  //Search ActionVector from the position after the entry value for Ptr to the end, and return true if find a Finish
16940  //entry before Fer or TimeLoc. No point checking for TimeTimeLoc since at a stop location now so a later TimeTimeLoc
16941  //must be preceded by a TimeLoc departure
16942  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsServiceTerminating");
16943  for(unsigned int x=1;x<TDEPtr->ActionVector.size();x++)
16944  {
16945  if((AVPtr + x) < TDEPtr->ActionVector.end())
16946  {
16947  AnsiString xx = (AVPtr + x)->Command;//test
16948  TTimetableFormatType xy = (AVPtr + x)->FormatType;//test
16949  TTimetableSequenceType xz = (AVPtr + x)->SequenceType;//test
16950  if(((AVPtr + x)->Command == "Fer") || ((AVPtr + x)->FormatType == TimeLoc))
16951  {
16952  Utilities->CallLogPop();
16953  return false;
16954  }
16955  else if((AVPtr + x)->SequenceType == Finish)
16956  {
16957  Utilities->CallLogPop();
16958  return true;
16959  }
16960  }
16961  }
16962  Utilities->CallLogPop();
16963  return false;
16964  }
16965 */
16966 // ---------------------------------------------------------------------------
16967 
16968 void TTrainController::SendPerformanceSummary(int Caller, std::ofstream &PerfFile)
16969 {
16970  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SendPerformanceSummary");
16971  AnsiString FormatStr = "####0.0";
16972  AnsiString AvLateArrMins = "";
16973  AnsiString AvEarlyArrMins = "";
16974  AnsiString AvLatePassMins = "";
16975  AnsiString AvEarlyPassMins = "";
16976  AnsiString AvLateDepMins = "";
16977 
16978  if(LateArrivals > 0)
16979  AvLateArrMins = FormatFloat(FormatStr, (TotLateArrMins / LateArrivals));
16980  if(EarlyArrivals > 0)
16981  AvEarlyArrMins = FormatFloat(FormatStr, (TotEarlyArrMins / EarlyArrivals));
16982  if(LatePasses > 0)
16983  AvLatePassMins = FormatFloat(FormatStr, (TotLatePassMins / LatePasses));
16984  if(EarlyPasses > 0)
16985  AvEarlyPassMins = FormatFloat(FormatStr, (TotEarlyPassMins / EarlyPasses));
16986  if(LateDeps > 0)
16987  AvLateDepMins = FormatFloat(FormatStr, (TotLateDepMins / LateDeps));
16988 
16989  PerfFile << '\n' << '\n' << "***************************************";
16990  PerfFile << '\n' << '\n' << "Performance summary:" << '\n';
16991 
16992  if(OnTimeArrivals != 1)
16993  PerfFile << OnTimeArrivals << " on-time arrivals" << '\n';
16994  else
16995  PerfFile << OnTimeArrivals << " on-time arrival" << '\n';
16996 
16997  if(LateArrivals > 1)
16998  PerfFile << LateArrivals << " late arrivals (average " << AvLateArrMins.c_str() << " min)" << '\n';
16999  else if(LateArrivals == 1)
17000  PerfFile << LateArrivals << " late arrival (" << AvLateArrMins.c_str() << " min)" << '\n';
17001  else
17002  PerfFile << LateArrivals << " late arrivals" << '\n';
17003 
17004  if(EarlyArrivals > 1)
17005  PerfFile << EarlyArrivals << " early arrivals (average " << AvEarlyArrMins.c_str() << " min)" << '\n';
17006  else if(EarlyArrivals == 1)
17007  PerfFile << EarlyArrivals << " early arrival (" << AvEarlyArrMins.c_str() << " min)" << '\n';
17008  else
17009  PerfFile << EarlyArrivals << " early arrivals" << '\n';
17010 
17011  if(OnTimePasses != 1)
17012  PerfFile << OnTimePasses << " on-time passes" << '\n';
17013  else
17014  PerfFile << OnTimePasses << " on-time pass" << '\n';
17015 
17016  if(LatePasses > 1)
17017  PerfFile << LatePasses << " late passes (average " << AvLatePassMins.c_str() << " min)" << '\n';
17018  else if(LatePasses == 1)
17019  PerfFile << LatePasses << " late pass (" << AvLatePassMins.c_str() << " min)" << '\n';
17020  else
17021  PerfFile << LatePasses << " late passes" << '\n';
17022 
17023  if(EarlyPasses > 1)
17024  PerfFile << EarlyPasses << " early passes (average " << AvEarlyPassMins.c_str() << " min)" << '\n';
17025  else if(EarlyPasses == 1)
17026  PerfFile << EarlyPasses << " early pass (" << AvEarlyPassMins.c_str() << " min)" << '\n';
17027  else
17028  PerfFile << EarlyPasses << " early passes" << '\n';
17029 
17030  if(OnTimeDeps != 1)
17031  PerfFile << OnTimeDeps << " on-time departures" << '\n';
17032  else
17033  PerfFile << OnTimeDeps << " on-time departure" << '\n';
17034 
17035  if(LateDeps > 1)
17036  PerfFile << LateDeps << " late departures (average " << AvLateDepMins.c_str() << " min)" << '\n';
17037  else if(LateDeps == 1)
17038  PerfFile << LateDeps << " late departure (" << AvLateDepMins.c_str() << " min)" << '\n';
17039  else
17040  PerfFile << LateDeps << " late departures" << '\n';
17041 
17042  TDateTime TempExcessLCDownTime;
17043  for(unsigned int x = 0; x < Track->BarriersDownVector.size(); x++) //added at v2.6.0 - should have been added earlier
17044  {
17045  if(Track->BarriersDownVector.at(x).ReducedTimePenalty)
17046  {
17047  TempExcessLCDownTime = TrainController->TTClockTime - Track->BarriersDownVector.at(x).StartTime - TDateTime(180.0 / 86400);
17048  }
17049  else
17050  {
17051  TempExcessLCDownTime = TrainController->TTClockTime - Track->BarriersDownVector.at(x).StartTime;
17052  }
17053  if(TempExcessLCDownTime > TDateTime(0))
17054  {
17055  TrainController->ExcessLCDownMins += (double(TempExcessLCDownTime) * 1440);
17056  }
17057  }
17058 
17059  AnsiString FormattedExcessLCDownMins = FormatFloat(FormatStr, ExcessLCDownMins);
17060 
17061  if(ExcessLCDownMins > 0.1)
17062  PerfFile << FormattedExcessLCDownMins.c_str() << " excess minutes of level crossing barrier down time" << '\n';
17063 
17064  if(MissedStops != 1)
17065  PerfFile << MissedStops << " missed stops" << '\n';
17066  else
17067  PerfFile << MissedStops << " missed stop" << '\n';
17068 
17069  if(OtherMissedEvents != 1)
17070  PerfFile << OtherMissedEvents << " other missed events" << '\n';
17071  else
17072  PerfFile << OtherMissedEvents << " other missed event" << '\n';
17073 
17074  if(UnexpectedExits != 1)
17075  PerfFile << UnexpectedExits << " unexpected train exits" << '\n';
17076  else
17077  PerfFile << UnexpectedExits << " unexpected train exit" << '\n';
17078 
17079  if(IncorrectExits != 1)
17080  PerfFile << IncorrectExits << " incorrect train exits" << '\n';
17081  else
17082  PerfFile << IncorrectExits << " incorrect train exit" << '\n';
17083 
17084  if(NumFailures != 1)
17085  PerfFile << NumFailures << " train failures" << '\n';
17086  else
17087  PerfFile << NumFailures << " train failure" << '\n';
17088 
17089  if(AvHoursIntValue > 0)
17090  {
17091  if(AvHoursIntValue == 1)
17092  {
17093  PerfFile << AvHoursIntValue << " hour mean time betweeen train failures" << '\n';
17094  }
17095  else
17096  {
17097  PerfFile << AvHoursIntValue << " hours mean time betweeen train failures" << '\n';
17098  }
17099  }
17100 
17101  AnsiString AvLateMinsLocsNotReached = "";
17102 
17104  int LocsNotReached = (NotStartedTrainArrDep + OperatingTrainArrDep) / 2;
17105 
17106  // each location has an arrival and departure (generally) so divide by 2
17107  if(LocsNotReached > 0)
17108  {
17109  AvLateMinsLocsNotReached = FormatFloat(FormatStr, (OperatingTrainLateMins + NotStartedTrainLateMins) / (NotStartedTrainArrDep + OperatingTrainArrDep));
17110  PerfFile << LocsNotReached << " locations that trains failed to reach (average lateness " << AvLateMinsLocsNotReached.c_str() << " min)" << '\n';
17111  }
17112 
17113  if(SPADRisks != 1)
17114  PerfFile << SPADRisks << " SPAD risks" << '\n';
17115  else
17116  PerfFile << SPADRisks << " SPAD risk" << '\n';
17117 
17118  if(SPADEvents != 1)
17119  PerfFile << SPADEvents << " SPADs" << '\n';
17120  else
17121  PerfFile << SPADEvents << " SPAD" << '\n';
17122 
17123  if(Derailments != 1)
17124  PerfFile << Derailments << " derailments" << '\n';
17125  else
17126  PerfFile << Derailments << " derailment" << '\n';
17127 
17128  if(CrashedTrains != 1)
17129  PerfFile << CrashedTrains << " crashed trains" << '\n';
17130  else
17131  PerfFile << CrashedTrains << " crashed train" << '\n';
17132 
17133  PerfFile << '\n' << "***************************************" << '\n';
17134 
17135  bool DerailSPADFlag = false, CrashFlag = false;
17136 
17137  int OverallScorePercent = 100;
17138  int TotArrDep = 0;
17139  double TotLateMinsFactor = 1;
17140  double MissedStopAndSPADRiskFactor = 1;
17141  double NetNegFactor = 1;
17142 
17144  // TotArrDep: total number of arrivals & departures including those for trains that haven't reached their destinations yet and are late
17145  // changed at v1.1.4 - calc was inside "if(OverallScorePercent == 100).." block so could remain 0 for SPADs & crashes, & then received the
17146  // 'no timetabled departures... message, which was inappropriate
17147 
17148  if((SPADEvents > 0) || (Derailments > 0))
17149  {
17150  OverallScorePercent = 5; // overrides other calculations
17151  DerailSPADFlag = true;
17152  }
17153  if(CrashedTrains > 0)
17154  {
17155  OverallScorePercent = 0; // overrides other calculations
17156  CrashFlag = true;
17157  }
17158  if(OverallScorePercent == 100)
17159  {
17160  if(TotArrDep > 0)
17161  {
17162  TotLateMinsFactor =
17164  ((OtherMissedEvents + UnexpectedExits + ExcessLCDownMins) * 15)) / TotArrDep);
17165  // TotLateMinsFactor: negative exponential factor based on overall average arr & dep minutes late (with OtherMissedEvents & UnexpectedExits
17166  // counting as 15 mins late each), where 4 mins late average = half, 8 mins late = a quarter etc
17167  MissedStopAndSPADRiskFactor = exp((-17.33) * (MissedStops + SPADRisks + IncorrectExits) / TotArrDep);
17168  // MissedEventAndSPADRiskFactor: negative exponential factor based on number of missed stops, SPAD risks & IncorrectExits as a proportion
17169  // of arrivals & departures, where 4% = half, 8% = a quarter etc
17170  NetNegFactor = TotLateMinsFactor * MissedStopAndSPADRiskFactor;
17171  // NetNegfactor: product of the above two
17172  OverallScorePercent = 100 * NetNegFactor;
17173  }
17174  }
17175  if((TotArrDep > 0) || DerailSPADFlag || CrashFlag)
17176  // flag condits added at v1.1.4 - see above for what the error was
17177  {
17178  AnsiString OneFailureString = ", though the failure would account for some poor performance";
17179  AnsiString TwoOrMoreFailureString = ", though the failures would account for some poor performance";
17180  AnsiString AddedString = "";
17181  if(NumFailures == 1)
17182  AddedString = OneFailureString;
17183  if(NumFailures > 1)
17184  AddedString = TwoOrMoreFailureString;
17185  PerfFile << "\nOverall score: " << OverallScorePercent << "%\n";
17186  AnsiString Rating = "";
17187  if(OverallScorePercent == 100)
17188  Rating = "Perfect!";
17189  else if(OverallScorePercent >= 95)
17190  Rating = "Excellent";
17191  else if(OverallScorePercent >= 90)
17192  Rating = "Very good";
17193  else if(OverallScorePercent >= 80)
17194  Rating = "Good";
17195  else if(OverallScorePercent >= 70)
17196  Rating = "Fair";
17197  else if(OverallScorePercent >= 60)
17198  Rating = "Unacceptable" + AddedString;
17199  else if(OverallScorePercent >= 50)
17200  Rating = "Poor" + AddedString;
17201  else if(OverallScorePercent >= 40)
17202  Rating = "Bad" + AddedString;
17203  else if(OverallScorePercent >= 30)
17204  Rating = "Very bad" + AddedString;
17205  else if(OverallScorePercent >= 20)
17206  Rating = "Terrible" + AddedString;
17207  else if(OverallScorePercent >= 10)
17208  Rating = "Appalling" + AddedString;
17209  else if(OverallScorePercent >= 5)
17210  {
17211  if(DerailSPADFlag)
17212  Rating = "Disastrous - potential loss of life";
17213  // SPADs/Derailments
17214  else
17215  Rating = "Dire" + AddedString;
17216  }
17217  else if(OverallScorePercent < 5)
17218  {
17219  if(CrashFlag)
17220  Rating = "Catastrophic - loss of life"; // Crashes
17221  else
17222  Rating = "Abysmal";
17223  }
17224  PerfFile << "Overall rating: " << Rating.c_str() << '\n';
17225  }
17226  else
17227  {
17228  PerfFile << "\nThere were no timetabled departures or arrivals so there is insufficient information to provide a performance score or rating" << '\n';
17229  }
17230  PerfFile << '\n' << "***************************************";
17231  Utilities->CallLogPop(1736);
17232 }
17233 
17234 // ---------------------------------------------------------------------------
17235 
17237 {
17238  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetWarningFlags");
17239  for(unsigned int x = 0; x < TrainVector.size(); x++)
17240  {
17241  TTrain &Train = TrainVectorAt(58, x);
17242  if(Train.Crashed)
17243  // can't use background colours for crashed & derailed because same colour
17244  {
17245  CrashWarning = true;
17246  }
17247  else if(Train.Derailed)
17248  // can't use background colours for crashed & derailed because same colour
17249  {
17250  DerailWarning = true;
17251  }
17252  else if(Train.BackgroundColour == clSPADBackground)
17253  // use colour as that changes as soon as passes signal
17254  {
17255  SPADWarning = true;
17256  }
17257  else if(Train.BackgroundColour == clTrainFailedBackground)
17258  {
17259  TrainFailedWarning = true;
17260  }
17261  else if(Train.BackgroundColour == clCallOnBackground)
17262  // use colour as also stopped at signal
17263  {
17264  CallOnWarning = true;
17265  }
17266  else if(Train.BackgroundColour == clSignalStopBackground)
17267  // use colour to distinguish from call-on
17268  {
17269  SignalStopWarning = true;
17270  }
17271  else if(Train.BackgroundColour == clBufferAttentionNeeded)
17272  // use colour to distinguish from ordinary buffer stop
17273  {
17274  BufferAttentionWarning = true;
17275  }
17276  }
17277  Utilities->CallLogPop(1796);
17278 }
17279 
17280 // ---------------------------------------------------------------------------
17281 
17283 {
17284  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CalcSignalStopLateness");
17285 
17286  // calculate lateness for running trains
17289  for(unsigned int x = 0; x < TrainVector.size(); x++)
17290  {
17291  TTrain &Train = TrainVectorAt(64, x);
17292  for(TActionVectorEntry * AVEntryPtr = &Train.TrainDataEntryPtr->ActionVector.front(); AVEntryPtr < &Train.TrainDataEntryPtr->ActionVector.back();
17293  AVEntryPtr++)
17294  {
17295  if(AVEntryPtr < Train.ActionVectorEntryPtr)
17296  continue;
17297  if((AVEntryPtr->ArrivalTime > TDateTime(-1)) && (GetRepeatTime(42, AVEntryPtr->ArrivalTime, Train.RepeatNumber, Train.IncrementalMinutes) <
17298  TTClockTime))
17299  {
17300  OperatingTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(43, AVEntryPtr->ArrivalTime, Train.RepeatNumber, Train.IncrementalMinutes));
17302  }
17303  if((AVEntryPtr->DepartureTime > TDateTime(-1)) && (GetRepeatTime(44, AVEntryPtr->DepartureTime, Train.RepeatNumber, Train.IncrementalMinutes) <
17304  TTClockTime))
17305  {
17306  OperatingTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(45, AVEntryPtr->DepartureTime, Train.RepeatNumber,
17307  Train.IncrementalMinutes));
17309  }
17310  }
17311  }
17312 
17313  // calculate lateness for trains that haven't started yet (could be held awaiting entry)
17316 
17317  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
17318  {
17319  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
17320  const TActionVectorEntry &AVEntryLast = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
17321  int IncrementalMinutes = 0;
17322  if(AVEntryLast.FormatType == Repeat)
17323  {
17324  IncrementalMinutes = AVEntryLast.RearStartOrRepeatMins;
17325  }
17326  for(int y = 0; y < TDEntry.NumberOfTrains; y++)
17327  {
17328  TTrainOperatingData &TTOD = TDEntry.TrainOperatingDataVector.at(y);
17329  if(TTOD.RunningEntry != NotStarted)
17330  continue;
17331  // note that can't rely on the above for sessionfiles saved before v0.6b as wasn't set to Running for Sns/Fsp/rsp & shuttles
17332  // but if trains had exited then would be set to Exited, so need to check against trains still operating - use the test below
17333  bool TrainOperatingFlag = false;
17334  for(unsigned int a = 0; a < TrainController->TrainVector.size(); a++)
17335  {
17336  if((TrainController->TrainVector.at(a).TrainDataEntryPtr == &TDEntry) && (TrainController->TrainVector.at(a).RepeatNumber == y))
17337  {
17338  TrainOperatingFlag = true;
17339  break;
17340  }
17341  }
17342  if(TrainOperatingFlag)
17343  continue;
17344 
17345  if(GetRepeatTime(46, TDEntry.ActionVector.at(0).EventTime, y, IncrementalMinutes) > TTClockTime)
17346  {
17347  break; // if the first time is greater than TTClockTime then all the rest will also be greater (& default of -1 will be less so will be ignored)
17348  }
17349  for(unsigned int z = 0; z < TDEntry.ActionVector.size(); z++)
17350  {
17351  TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(z);
17352  if(GetRepeatTime(35, AVEntry.EventTime, y, IncrementalMinutes) > TTClockTime)
17353  {
17354  break; // all the rest will also be greater (& default of -1 will be less)
17355  }
17356  if(GetRepeatTime(36, AVEntry.ArrivalTime, y, IncrementalMinutes) > TTClockTime)
17357  {
17358  break; // all the rest will also be greater (& default of -1 will be less)
17359  }
17360  if(GetRepeatTime(37, AVEntry.DepartureTime, y, IncrementalMinutes) > TTClockTime)
17361  {
17362  break; // all the rest will also be greater (& default of -1 will be less)
17363  }
17364  if((AVEntry.ArrivalTime > TDateTime(-1)) && (GetRepeatTime(38, AVEntry.ArrivalTime, y, IncrementalMinutes) < TTClockTime))
17365  {
17366  NotStartedTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(39, AVEntry.ArrivalTime, y, IncrementalMinutes));
17368  }
17369  if((AVEntry.DepartureTime > TDateTime(-1)) && (GetRepeatTime(40, AVEntry.DepartureTime, y, IncrementalMinutes) < TTClockTime))
17370  {
17371  NotStartedTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(41, AVEntry.DepartureTime, y, IncrementalMinutes));
17373  }
17374  }
17375  }
17376  }
17377  Utilities->CallLogPop(1894);
17378 }
17379 
17380 // ---------------------------------------------------------------------------
17381 
17383  // new v2.2.0 for OperatorActionPanel
17384  // clears entries then adds values for running trains then for continuation entries
17385  // dont limit size here as need to check all trains (OAListBox is limited to 20 trains in Interface.cpp)
17386 {
17387  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RebuildOpTimeToActMultimap");
17388  OpTimeToActMultiMap.clear();
17389  TOpTimeToActMultiMapEntry OpTimeToActMultiMapEntry;
17390 
17391  if(!TrainVector.empty())
17392  // build OpTimeToActMultiMap entries for running trains
17393  {
17394  AnsiString HeadCode;
17395  // dropped in favour of TrainID for running trains int VecPos; //TrackVectorPosition of LeadElement or continuation where train is to enter
17396  int TrainID;
17397  THCandTrainPosParam HCandTrainPosParam;
17398  for(unsigned int x = 0; x < TrainVector.size(); x++)
17399  {
17400  HeadCode = TrainVectorAt(62, x).HeadCode;
17401  TrainID = TrainVectorAt(63, x).TrainID;
17402  HCandTrainPosParam.first = HeadCode;
17403  HCandTrainPosParam.second = TrainID;
17404  float TimeToAct = TrainVectorAt(65, x).OpTimeToAct;
17405  if((TimeToAct >= 0) && (TimeToAct < 59.9))
17406  // -1 indicates don't display
17407  {
17408  OpTimeToActMultiMapEntry.first = TimeToAct;
17409  OpTimeToActMultiMapEntry.second = HCandTrainPosParam;
17410  OpTimeToActMultiMap.insert(OpTimeToActMultiMapEntry);
17411  }
17412  }
17413  }
17414 
17415 /*
17416  * class TContinuationTrainExpectationEntry
17417  {
17418  public:
17419  AnsiString Description; ///< service description
17420  AnsiString HeadCode; ///< service headcode
17421  int RepeatNumber; ///< service RepeatNumber
17422  int IncrementalMinutes; ///< Repeat separation in minutes
17423  int IncrementalDigits; ///< Repeat headcode separation
17424  int VectorPosition; ///< TrackVectorPosition for the continuation element
17425  TTrainDataEntry *TrainDataEntryPtr; ///< points to the service entry in the timetable's TrainDataVector
17426  };
17427 
17428  Multimap class for TContinuationTrainExpectationEntry objects, where the access key is the expectation time
17429  typedef std::multimap<TDateTime, TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMap;
17430  typedef TContinuationTrainExpectationMultiMap::iterator TContinuationTrainExpectationMultiMapIterator; ///< iterator for the multimap
17431  typedef std::pair<TDateTime, TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMapPair; ///< a single multimap entry
17432 */
17433 
17435  // build OpTimeToActMultiMap entries for expected trains
17436  {
17437  // note that using the ContinuationTrainExpectationMultiMap automatically ensures that entries are in ascending time order
17438  // first have to calculate times to red signal for each train due to enter (ignore later trains as will likely change before they are due)
17439  float TimeToAct = 0; // minutes
17440  int DistanceToRedSignal = 0; // metres
17441  TContinuationEntryVecPosVector ContinuationEntryVecPosVector;
17442  // used to ensure only one train displayed for a given continuation
17443  ContinuationEntryVecPosVector.clear();
17444  bool LaterTrain = false;
17447  {
17448  LaterTrain = false;
17449  if(CTEIt->second.TrainDataEntryPtr->TrainOperatingDataVector.at(CTEIt->second.RepeatNumber).RunningEntry != NotStarted)
17450  {
17451  CTEIt++;
17452  continue; // not interested in running or exited trains
17453  }
17454  if(Track->TrackElementAt(934, CTEIt->second.VectorPosition).TrainIDOnElement > 0)
17455  {
17456  CTEIt++;
17457  continue;
17458  // don't include trains not entered yet when a train is already on the continuation
17459  }
17460  if(!ContinuationEntryVecPosVector.empty())
17461  {
17462  for(unsigned int x = 0; x < ContinuationEntryVecPosVector.size(); x++)
17463  {
17464  if(CTEIt->second.VectorPosition == ContinuationEntryVecPosVector.at(x))
17465  {
17466  LaterTrain = true; ;
17467  // skip past remaining trains waiting to enter at same point
17468  break;
17469  }
17470  }
17471  }
17472  if(LaterTrain)
17473  {
17474  CTEIt++;
17475  continue;
17476  }
17477  ContinuationEntryVecPosVector.push_back(CTEIt->second.VectorPosition);
17478  AnsiString HeadCode = CTEIt->second.HeadCode;
17479  float CurrentStopTime; // set to 0 at start of function
17480  float LaterStopTime; // set to 0 at start of function
17481  float RecoverableTime; // set to 0 at start of function
17482  int AvTrackSpeed; // set to 0 at start of function
17483  int TrainID = -1; // not yet allocated for train still to enter
17484  bool SigControlAndCanPassRedSignal = false;
17485  // doesn't apply for a continuation
17486  DistanceToRedSignal = CalcDistanceToRedSignalandStopTime(1, CTEIt->second.VectorPosition, 0,
17487  // EntryPos always 0 for entering at a continuation
17488  SigControlAndCanPassRedSignal, &CTEIt->second.TrainDataEntryPtr->ActionVector.at(1),
17489  // at(1) to skip past the Start train value
17490  HeadCode, TrainID, CurrentStopTime, LaterStopTime, RecoverableTime, AvTrackSpeed);
17491  // for above VectorPosition is the first element to have its length included in the sum, so for a continuation it's the continuation itself
17492  // for a train it's the one in front of LeadElement
17493  if(AvTrackSpeed < 30)
17494  AvTrackSpeed = 30;
17495  if(DistanceToRedSignal == -1)
17496  {
17497  TimeToAct = 60.0;
17498  }
17499  else
17500  {
17501  int Speed = AvTrackSpeed;
17502  int MaxSpeed = int(CTEIt->second.TrainDataEntryPtr->MaxRunningSpeed);
17503  if(AvTrackSpeed > MaxSpeed)
17504  Speed = MaxSpeed;
17505  if(CTEIt->second.TrainDataEntryPtr->ActionVector.at(1).SignallerControl)
17506  // defined in timetable as under signaller control
17507  {
17508  Speed = CTEIt->second.TrainDataEntryPtr->SignallerSpeed;
17509  LaterStopTime = 0;
17510  }
17511  TimeToAct = LaterStopTime + DistanceToRedSignal * 3.6 / 60 / Speed;
17512  // accel & decel taken into account in
17513  // CalcDistanceToRedSignalandStopTime
17514  // 3.6 convertsKm/h to m/s & 60 converts seconds to minutes
17515  // don't need CurrentStopTime or RecoverableTime for continuation entries
17516  float MinsBefEnter = double(CTEIt->first - TTClockTime) * 86400.0 / 60.0;
17517  TimeToAct += MinsBefEnter;
17518  }
17519  THCandTrainPosParam HCandTrainPosParam;
17520  HCandTrainPosParam.first = HeadCode;
17521  HCandTrainPosParam.second = -1 - CTEIt->second.VectorPosition;
17522  // -1-CTE... because 2nd value covers TrainID if +ve &
17523  // continuation track vector position if -ve, -1 allows for vecpos being 0
17524  if(TimeToAct < 59.9) // if 60 don't enter a value in multimap
17525  {
17526  OpTimeToActMultiMapEntry.first = TimeToAct;
17527  OpTimeToActMultiMapEntry.second = HCandTrainPosParam;
17528  OpTimeToActMultiMap.insert(OpTimeToActMultiMapEntry);
17529  }
17530  CTEIt++;
17531  }
17532  }
17533  Utilities->CallLogPop(2081);
17534 }
17535 
17536 // ---------------------------------------------------------------------------
17537 
17538 int TTrainController::CalcDistanceToRedSignalandStopTime(int Caller, int TrackVectorPosition, int TrackVectorPositionEntryPos,
17539  bool SigControlAndCanPassRedSignal, TActionVectorEntry *AVPtr, AnsiString HeadCode, int TrainID, float &CurrentStopTime, float &LaterStopTime,
17540  float &RecoverableTime, int &AvTrackSpeed)
17541  // new v2.2.0
17542  // vectorPosition is the value for the first element to be measured - for a continuation it's the continuation itself, for a train
17543  // it's the one after LeadElement, returns -1 for infinity - i.e. not approaching red signal (e.g. maybe cdt before reach it).
17544  // CurrentStopTime is the time to depart from the current station (if stopped at a station) and LaterStopTime is the total station
17545  // stop times for stations after the current one. DistanceToRedSignal is what the name implies, if -1 is returned the other values
17546  // aren't used - this means there is no display for the train in question
17547 {
17548  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",DistanceToRedSignal, " + AnsiString(TrackVectorPosition) + ", " +
17549  AnsiString(TrackVectorPositionEntryPos) + ", " + AVPtr->Command);
17550  int DistanceToRedSignal = 0;
17551  int CumTrackSpeed = 0;
17552  // average track speed, in case need to use in time calc
17553  int TrackSpeedCount = 0;
17554 
17555  //below added at v2.6.1
17556  if(TrainID > -1)
17557  {
17558  TTrain &Train = TrainVectorAtIdent(51, TrainID);
17559  Train.DistanceToStationStop = 0;//if find a red signal first then this distance isn't needed
17560  Train.StationStopCalculated = false;
17561  }
17562 
17563  AvTrackSpeed = 0;
17564  int CurrentElement = TrackVectorPosition;
17565  int CurrentEntryPos = TrackVectorPositionEntryPos;
17566  int NextElement;
17567  int NextEntryPos;
17568  int NextExitPos;
17569 
17570  CurrentStopTime = 0;
17571  LaterStopTime = 0;
17572  RecoverableTime = 0;
17573  if(CurrentElement == -1) // end element, no action needed
17574  {
17575  Utilities->CallLogPop(2094);
17576  return -1;
17577  }
17578  int CurrentExitPos;
17579 
17580  // get ExitPos for first element to be measured
17581  if(Track->TrackElementAt(935, CurrentElement).TrackType == Points)
17582  {
17583  if((CurrentEntryPos == 0) || (CurrentEntryPos == 2)) // leading point
17584  {
17585  if(Track->TrackElementAt(936, CurrentElement).Attribute == 0)
17586  CurrentExitPos = 1;
17587  else
17588  CurrentExitPos = 3;
17589  }
17590  else
17591  CurrentExitPos = 0; // trailing point
17592  }
17593  else
17594  CurrentExitPos = Track->GetNonPointsOppositeLinkPos(CurrentEntryPos);
17595  // get CumTrackSpeed for first measured element
17596 
17597  TConfiguration CurrentExitConfig = Track->TrackElementAt(937, CurrentElement).Config[CurrentExitPos];
17598  int CurrentAttribute = Track->TrackElementAt(938, CurrentElement).Attribute;
17599 
17600  // check if currently stopped at a location, and if so add the remaining dwell time
17601  // can't use CurrentElement as that is in front of LeadElement and might not be at the location
17602  if(TrainID > -1)
17603  // -1 for a continuation and can't be at a location as not yet entered
17604  {
17605  TTrain &Train = TrainVectorAtIdent(39, TrainID); //Train wasn't a reference before v2.6.1 mods so FirstLaterStopRecoverableTime wouldn't be reset for the referenced train
17607  // this used to deduct from RecoverableTime when arrive at a location
17608  if(Train.StoppedAtLocation)
17609  {
17610  if(Train.StoppedForTrainInFront)
17611  {
17612  Utilities->CallLogPop(2082);
17613  return -1; // no action needed
17614  }
17615  else if(!((Train.ActionVectorEntryPtr->FormatType == TimeTimeLoc) || (Train.ActionVectorEntryPtr->FormatType == TimeLoc)))
17616  {
17617  Utilities->CallLogPop(2083);
17618  return -1; // not due a departure so no action needed
17619  }
17620  else // due a departure
17621  {
17622  double TimeToDepart = double(Train.ReleaseTime - TrainController->TTClockTime) * 86400 / 60; // mins to depart
17623  // can't convert a TDateTime to a float directly
17624  CurrentStopTime = float(TimeToDepart);
17625  AVPtr++;
17626  }
17627  }
17628  }
17629 
17630  // check if CurrentElement is a red signal, but ok if autosig route after
17631  if((CurrentExitConfig == Signal) && (CurrentAttribute == 0))
17632  // ok if autosig route after red signal
17633  {
17634  int NextElement = Track->TrackElementAt(939, CurrentElement).Conn[CurrentExitPos];
17635  int NextEntryPos = Track->TrackElementAt(940, CurrentElement).ConnLinkPos[CurrentExitPos];
17636  int RouteNumber; // holder for referenced value, not used
17637  if(AllRoutes->GetRouteTypeAndNumber(33, NextElement, NextEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
17638  {
17639  Utilities->CallLogPop(2078);
17640  return -1;
17641  }
17642  else if(SigControlAndCanPassRedSignal)
17643  // ignore signal and increment CurrentElement to NextElement
17644  {
17645  if(Track->TrackElementAt(941, NextElement).TrackType == Points)
17646  {
17647  if((NextEntryPos == 0) || (NextEntryPos == 2))
17648  // leading entry point
17649  {
17650  if(Track->TrackElementAt(942, NextElement).Attribute == 0)
17651  NextExitPos = 1;
17652  else
17653  NextExitPos = 3;
17654  }
17655  else
17656  NextExitPos = 0; // trailing entry point
17657  }
17658  else
17659  NextExitPos = Track->GetNonPointsOppositeLinkPos(NextEntryPos);
17660 
17661  CurrentElement = NextElement;
17662  CurrentEntryPos = NextEntryPos;
17663  CurrentExitPos = NextExitPos;
17664  CurrentExitConfig = Track->TrackElementAt(943, CurrentElement).Config[CurrentExitPos];
17665  CurrentAttribute = Track->TrackElementAt(944, CurrentElement).Attribute;
17666  }
17667  else if((TrainID > -1) && (TrainVectorAtIdent(40, TrainID).TrainMode == Timetable)) // ignore signallercontrol or will
17668  // give 'NOW' indication after allowed to pass red signal when LeadMidLag (AllowedToPassRedSignal reset by this point)
17669  {
17670  Utilities->CallLogPop(2084);
17671  return 0;
17672  // stopped with red signal in front, don't need AvSpeedLimit in this case, & if at location awaiting departure dwell time already calculated
17673  }
17674  }
17675  int LaterStopNumber = 0;
17676  int x = 0;
17677  // added in v2.4.0 to prevent endless circling round track loops - spotted by Xeon 09/03/20 & reported by emsil
17678 
17679  while(!((CurrentExitConfig == Signal) && (CurrentAttribute == 0)))
17680  // not red signal next (in fwd direction) so enter loop to calc CumLength
17681  {
17682  x++; // added in v2.4.0 as above
17683  if(x > 5000)
17684  {
17685  Utilities->CallLogPop(2120);
17686  return -1;
17687  }
17688  if(CurrentEntryPos > 1)
17689  {
17690  DistanceToRedSignal += Track->TrackElementAt(916, CurrentElement).Length23;
17691  CumTrackSpeed += Track->TrackElementAt(945, CurrentElement).SpeedLimit23;
17692  }
17693  else
17694  {
17695  DistanceToRedSignal += Track->TrackElementAt(917, CurrentElement).Length01;
17696  CumTrackSpeed += Track->TrackElementAt(946, CurrentElement).SpeedLimit01;
17697  }
17698  TrackSpeedCount++;
17699 
17700  // added at v2.6.1 to find DistanceToStationStop for trains running early
17701  if(TrainID > -1) //can ignore continuation entries as these don't run early
17702  {
17703  TTrain &Train = TrainVectorAtIdent(52, TrainID);
17704  if(!Train.StationStopCalculated)
17705  {
17706  if(Train.TrainMode == Timetable)
17707  {
17708  bool StopRequired = false;
17709  if(!Train.TimetableFinished && (Train.NameInTimetableBeforeCDT(16, Track->TrackElementAt(1005, CurrentElement).ActiveTrackElementName,
17710  StopRequired) > -1) && ((Track->TrackElementAt(1006, CurrentElement).StationEntryStopLinkPos1 == CurrentEntryPos) ||
17711  (Track->TrackElementAt(1010, CurrentElement).StationEntryStopLinkPos2 == CurrentEntryPos)))
17712  {
17713  // no need to add in the length of element to CumulativeLength
17714  if(StopRequired)
17715  {
17716  Train.DistanceToStationStop = DistanceToRedSignal;// DistanceToRedSignal holds the intermediate distance to this point
17717  Train.StationStopCalculated = true; //don't want to update it with later stops
17718  }
17719  }
17720  }
17721  }
17722  }
17723 
17724  // check for train in front, but if on a bridge on other track then ok
17725  TTrackElement TE = Track->TrackElementAt(947, CurrentElement);
17726  int TrainOnElement;
17727  if(TE.TrackType != Bridge)
17728  {
17729  TrainOnElement = TE.TrainIDOnElement;
17730  }
17731  else
17732  {
17733  if(CurrentEntryPos > 1)
17734  TrainOnElement = TE.TrainIDOnBridgeTrackPos23;
17735  else
17736  TrainOnElement = TE.TrainIDOnBridgeTrackPos01;
17737  }
17738  if((TrainOnElement > -1) && (TrainOnElement != TrainID))
17739  // train in front before red signal
17740  {
17741  Utilities->CallLogPop(2085);
17742  return -1;
17743  }
17744 
17745  // add to stoptime if required
17746  if(Track->TrackElementAt(948, CurrentElement).ActiveTrackElementName != "")
17747  {
17748  double StopTimeDouble;
17749  while(AVPtr->FormatType == PassTime)
17750  AVPtr++; // skip past any passes
17751  if((Track->TrackElementAt(949, CurrentElement).ActiveTrackElementName == AVPtr->LocationName) && ((AVPtr->FormatType == TimeLoc) ||
17752  (AVPtr->FormatType == TimeTimeLoc)))
17753  // stop due here so calc dwell time & advance Ptr
17754  {
17755  if(AVPtr->FormatType == TimeTimeLoc)
17756  {
17757  LaterStopNumber++;
17758  StopTimeDouble = double(AVPtr->DepartureTime - AVPtr->ArrivalTime) * 86400.0 / 60.0;
17759  if(StopTimeDouble < 0.5)
17760  StopTimeDouble = 0.5;
17761  // at least 30 secs delay at station
17762  // can't convert a TDateTime to a float directly
17763  LaterStopTime += float(StopTimeDouble);
17764  RecoverableTime += StopTimeDouble - 0.5;
17765  if((LaterStopNumber == 1) && (TrainID > -1))
17766  TrainVectorAtIdent(41, TrainID).FirstLaterStopRecoverableTime = RecoverableTime;
17767  AVPtr++;
17768  }
17769  else if((AVPtr->FormatType == TimeLoc) && (AVPtr->ArrivalTime != TDateTime(-1))) // must be an arrival
17770  {
17771  if((AVPtr + 1)->FormatType == TimeLoc)
17772  // must be a departure
17773  {
17774  LaterStopNumber++;
17775  StopTimeDouble = double((AVPtr + 1)->DepartureTime - AVPtr->ArrivalTime) * 86400.0 / 60.0;
17776  // can't convert a TDateTime to a float directly
17777  if(StopTimeDouble < 0.5)
17778  StopTimeDouble = 0.5;
17779  // at least 30 secs delay at station
17780  LaterStopTime += float(StopTimeDouble);
17781  RecoverableTime += StopTimeDouble - 0.5;
17782  if((LaterStopNumber == 1) && (TrainID > -1))
17783  TrainVectorAtIdent(42, TrainID).FirstLaterStopRecoverableTime = RecoverableTime;
17784  AVPtr++;
17785  AVPtr++;
17786  }
17787  else // not a departure, does something else at the location so no calculation needed
17788  {
17789  Utilities->CallLogPop(2086);
17790  return -1;
17791  }
17792  }
17793  }
17794  }
17795 
17796  NextElement = Track->TrackElementAt(950, CurrentElement).Conn[CurrentExitPos];
17797  if(NextElement == -1) // reached end element, no action needed
17798  {
17799  Utilities->CallLogPop(2077);
17800  return -1;
17801  }
17802  NextEntryPos = Track->TrackElementAt(919, CurrentElement).ConnLinkPos[CurrentExitPos];
17803  // get NextExitPos
17804  if(Track->TrackElementAt(920, NextElement).TrackType == Points)
17805  {
17806  if((NextEntryPos == 0) || (NextEntryPos == 2))
17807  // leading entry point
17808  {
17809  if(Track->TrackElementAt(921, NextElement).Attribute == 0)
17810  NextExitPos = 1;
17811  else
17812  NextExitPos = 3;
17813  }
17814  else
17815  NextExitPos = 0; // trailing entry point
17816  }
17817  else
17818  NextExitPos = Track->GetNonPointsOppositeLinkPos(NextEntryPos);
17819 
17820  CurrentElement = NextElement;
17821  CurrentEntryPos = NextEntryPos;
17822  CurrentExitPos = NextExitPos;
17823  CurrentExitConfig = Track->TrackElementAt(922, CurrentElement).Config[CurrentExitPos];
17824  CurrentAttribute = Track->TrackElementAt(923, CurrentElement).Attribute;
17825  }
17826  if((CurrentExitConfig == Signal) && (CurrentAttribute == 0))
17827  // ok if autosig route after red signal, no action needed
17828  {
17829  int NextElement = Track->TrackElementAt(924, CurrentElement).Conn[CurrentExitPos];
17830  int NextEntryPos = Track->TrackElementAt(925, CurrentElement).ConnLinkPos[CurrentExitPos];
17831  int RouteNumber; // holder for referenced value, not used
17832  if(AllRoutes->GetRouteTypeAndNumber(31, NextElement, NextEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
17833  {
17834  Utilities->CallLogPop(2095);
17835  return -1;
17836  }
17837  }
17838  // Av speed calculation based on formula: Speed = 8.75*(kms/location stop) + 44 km/sec (from experiments), subject to a maximum of
17839  // average line speed/2 (for half distance accelerating and half decelerating.
17840 
17841  float MaxAllowableSpeed;
17842 
17843  if(TrackSpeedCount > 0)
17844  MaxAllowableSpeed = CumTrackSpeed / TrackSpeedCount;
17845  else // shouldn't reach here but include to prevent divide by zero error
17846  {
17847  if(CurrentEntryPos > 1)
17848  {
17849  MaxAllowableSpeed = Track->TrackElementAt(951, CurrentElement).SpeedLimit23;
17850  }
17851  else
17852  {
17853  MaxAllowableSpeed = Track->TrackElementAt(952, CurrentElement).SpeedLimit01;
17854  }
17855  // Train MaxRunningSpeed taken into account in RebuildOpTimeToActMultimap
17856  }
17857  float KmPerLocationStop;
17858 
17859  if(LaterStopNumber > 0)
17860  {
17861  KmPerLocationStop = float(DistanceToRedSignal) / LaterStopNumber / 1000; // m to km
17862  AvTrackSpeed = (8.75 * KmPerLocationStop) + 44;
17863  }
17864  else
17865  {
17866  AvTrackSpeed = (sqrt(float(DistanceToRedSignal) / 1000) * 44) + 60;
17867  // using linear trendline for accel & decel distance at various speeds
17868  // at half braking, speed never < 60 using this
17869  }
17870  if(AvTrackSpeed > MaxAllowableSpeed)
17871  AvTrackSpeed = MaxAllowableSpeed;
17872  Utilities->CallLogPop(2096);
17873  return DistanceToRedSignal;
17874 }
17875 
17876 // ---------------------------------------------------------------------------
17877 // end of TTrainController entries
17878 // ---------------------------------------------------------------------------
TTrain::LinkOccupied
bool LinkOccupied(int Caller, int TrackVectorPosition, int LinkNumber)
Added at v1.2.0: true if any part of train on specific link, false otherwise, including no link prese...
Definition: TrainUnit.cpp:7642
TAllRoutes::TrackIsInARoute
bool TrackIsInARoute(int Caller, int TrackVectorPosition, int LinkPos)
Examines Route2MultiMap and if the element at TrackVectorPosition with LinkPos (can be entry or exit)...
Definition: TrackUnit.cpp:16268
TActionVectorEntry::EventTime
TDateTime EventTime
Definition: TrainUnit.h:106
TTrain::AllowedToPassRedSignal
bool AllowedToPassRedSignal
set when train has been called on, or when under signaller control and instructed to pass a red signa...
Definition: TrainUnit.h:339
JoinedByOther
@ JoinedByOther
Definition: TrainUnit.h:49
TTrainController::CheckShuttleRepeatTime
bool CheckShuttleRepeatTime(int Caller, TDateTime ForwardEventTime, TDateTime ReverseEventTime, int RepeatMinutes)
Check that shuttle link services have consistent times, true for success.
Definition: TrainUnit.cpp:13264
TTrain::ZeroPowerNoNewShuttleFromNonRepeatMessage
bool ZeroPowerNoNewShuttleFromNonRepeatMessage
Definition: TrainUnit.h:306
TTrain::ZeroPowerNoNewServiceMessage
bool ZeroPowerNoNewServiceMessage
Definition: TrainUnit.h:305
TTrain::VOffset
int VOffset[4]
each headcode character is an 8x8 pixel graphic and must be placed within a 16x16 pixel element,...
Definition: TrainUnit.h:435
TTrainController
Handles all train and timetable activities, only one object created.
Definition: TrainUnit.h:635
TTrain::TTrain
TTrain(int Caller, int RearStartElementIn, int RearStartExitPosIn, AnsiString InputCode, int StartSpeed, int Mass, double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, TTrainMode TrainMode, TTrainDataEntry *TrainDataEntryPtr, int RepeatNumber, int IncrementalMinutes, int IncrementalDigits, int SignallerMaxSpeed)
Constructor, sets listed member values.
Definition: TrainUnit.cpp:62
TAllRoutes::LockedRouteVector
TLockedRouteVector LockedRouteVector
the vector that stores all the locked routes on the railway
Definition: TrackUnit.h:1586
TRailGraphics::smOrange
Graphics::TBitmap * smOrange
Definition: GraphicUnit.h:883
TActionVector
std::vector< TActionVectorEntry > TActionVector
contains all actions for a single train
Definition: TrainUnit.h:144
TUtilities::LoadFileString
AnsiString LoadFileString(std::ifstream &InFile)
loads a string value from the file
Definition: Utilities.cpp:182
TTrainController::CheckNonRepeatingShuttleLinksAndSetData
bool CheckNonRepeatingShuttleLinksAndSetData(int Caller, AnsiString MainHeadCode, AnsiString NonRepeatingHeadCode, bool GiveMessages)
A timetable validation function where cross references are checked for validity for non-repeating shu...
Definition: TrainUnit.cpp:13287
TTrainController::CheckStartPositionValidity
bool CheckStartPositionValidity(int Caller, AnsiString RearElementStr, AnsiString FrontElementStr, bool GiveMessages)
A timetable validation function where train starting positions are checked for validity,...
Definition: TrainUnit.cpp:12976
TTrain::MidEntryPos
int MidEntryPos
Definition: TrainUnit.h:327
TTrainController::RebuildOpTimeToActMultimap
void RebuildOpTimeToActMultimap(int Caller)
new v2.2.0 for OperatorActionPanel
Definition: TrainUnit.cpp:17382
TTrainController::PwrHigh
bool PwrHigh
Definition: TrainUnit.h:743
TTrainController::BuildContinuationTrainExpectationMultiMap
void BuildContinuationTrainExpectationMultiMap(int Caller)
populate the ContinuationTrainExpectationMultiMap during timetable loading
Definition: TrainUnit.cpp:14300
TTrain::CheckNewServiceDepartureTime
AnsiString CheckNewServiceDepartureTime(int Caller, TActionVectorEntry *Ptr, int RptNum, TTrainDataEntry *LinkedTrainDataPtr, AnsiString RetStr)
called during FloatingLabelNextString to find the next service departure time
Definition: TrainUnit.cpp:6541
TFixedTrackPiece::GraphicPtr
Graphics::TBitmap * GraphicPtr
the track bitmap for display on the zoomed-in railway
Definition: TrackUnit.h:90
SignallerMoveForwards
@ SignallerMoveForwards
Definition: TrainUnit.h:50
TRailGraphics::CodeR
Graphics::TBitmap * CodeR
Definition: GraphicUnit.h:979
Create
@ Create
Definition: TrainUnit.h:49
TTrainController::TContinuationEntryVecPosVector
std::vector< int > TContinuationEntryVecPosVector
ensures only one train displayed for a given continuation
Definition: TrainUnit.h:722
TAllRoutes::SetAllRearwardsSignals
void SetAllRearwardsSignals(int Caller, int Attribute, int RouteNumber, int RouteStartPosition)
Set rearwards signals from the specified route starting position.
Definition: TrackUnit.cpp:17277
Arrive
@ Arrive
Definition: TrainUnit.h:49
ChangeDirection
@ ChangeDirection
Definition: TrainUnit.h:49
TTrain::ZeroPowerNoCDTMessage
bool ZeroPowerNoCDTMessage
Definition: TrainUnit.h:304
TTrain::CallingOnFlag
bool CallingOnFlag
calling on permitted
Definition: TrainUnit.h:345
Depart
@ Depart
Definition: TrainUnit.h:49
TRailGraphics::gl89set
Graphics::TBitmap * gl89set
Definition: GraphicUnit.h:701
TTrainController::CheckHeadCodeValidity
bool CheckHeadCodeValidity(int Caller, bool GiveMessages, AnsiString HeadCode)
Returns true if the headcode complies with requirements.
Definition: TrainUnit.cpp:10208
TRailGraphics::gl88set
Graphics::TBitmap * gl88set
Definition: GraphicUnit.h:699
clBufferStopBackground
#define clBufferStopBackground
Definition: GraphicUnit.h:291
TTrain::MaxRunningSpeed
double MaxRunningSpeed
the current maximum train running speed
Definition: TrainUnit.h:385
TTrack::BarriersDownVector
TActiveLCVector BarriersDownVector
vector of LCs with barriers down
Definition: TrackUnit.h:689
TPrefDirElement::GetXLinkPos
int GetXLinkPos() const
Returns the XLink array position.
Definition: TrackUnit.h:289
TTrack::IsLCBarrierDownAtHV
bool IsLCBarrierDownAtHV(int Caller, int HLoc, int VLoc)
True if an open (to trains) level crossing is found at H & V.
Definition: TrackUnit.cpp:6566
TAllRoutes::TLockedRouteClass::TruncateTrackVectorPosition
unsigned int TruncateTrackVectorPosition
the TrackVector position of the element selected for truncation
Definition: TrackUnit.h:1504
RestoreTimetableControl
@ RestoreTimetableControl
Definition: TrainUnit.h:50
FailCrashed
@ FailCrashed
Definition: TrainUnit.h:41
TRailGraphics::TempHeadCode
Graphics::TBitmap * TempHeadCode
Definition: GraphicUnit.h:889
TAllRoutes::AutoSigsRoute
@ AutoSigsRoute
Definition: TrackUnit.h:1516
TTrack::TSigElement::Attribute
int Attribute
the signal state - red, yellow, double yellow or green
Definition: TrackUnit.h:625
TActionVectorEntry::LocationType
TTimetableLocationType LocationType
indicates where the train is when the relevant action occurs
Definition: TrainUnit.h:112
TTrack::GapFlashGreenPosition
int GapFlashGreenPosition
Definition: TrackUnit.h:673
TAllRoutes::TRouteElementPair
std::pair< int, unsigned int > TRouteElementPair
defines a specific element in a route, the first (int) value is the vector position in the AllRoutesV...
Definition: TrackUnit.h:1527
NamedNonStationLocation
@ NamedNonStationLocation
Definition: TrackUnit.h:64
FinRemHere
@ FinRemHere
Definition: TrainUnit.h:62
TTrain::HeadCodeGrPtr
Graphics::TBitmap * HeadCodeGrPtr[4]
points to the headcode segment graphics e.g. 5,A,4,7.
Definition: TrainUnit.h:452
TTrainController::TContinuationTrainExpectationEntry::TrainDataEntryPtr
TTrainDataEntry * TrainDataEntryPtr
points to the service entry in the timetable's TrainDataVector
Definition: TrainUnit.h:683
TTrain::ChangeTrainDirection
void ChangeTrainDirection(int Caller)
Reverses the direction of motion of the train.
Definition: TrainUnit.cpp:5731
FSHNewService
@ FSHNewService
Definition: TrainUnit.h:63
TTrain::MaxBrakeRate
double MaxBrakeRate
the maximum brake rate that the train can achieve
Definition: TrainUnit.h:389
TTimetableLocationType
TTimetableLocationType
Definition: TrainUnit.h:66
TAllRoutes::DiagonalFouledByRoute
bool DiagonalFouledByRoute(int Caller, int HLoc, int VLoc, int DiagonalLinkNumber)
As above but only checks for a route (may or may not be a train present (new at v1....
Definition: TrackUnit.cpp:17997
TTrainController::CheckForDuplicateCrossReferences
bool CheckForDuplicateCrossReferences(int Caller, AnsiString MainHeadCode, AnsiString SecondHeadCode, bool GiveMessages)
A timetable validation function where referenced services are checked for uniqueness,...
Definition: TrainUnit.cpp:12250
TTrackElement::StationEntryStopLinkPos2
int StationEntryStopLinkPos2
Used for track at platforms and non-station named locations to mark the train front element stop posi...
Definition: TrackUnit.h:149
TTrain::MidExitPos
int MidExitPos
Definition: TrainUnit.h:327
TRailGraphics::CodeD
Graphics::TBitmap * CodeD
Definition: GraphicUnit.h:965
TTrain::TrainFailed
bool TrainFailed
added at v2.4.0 to indicate failure
Definition: TrainUnit.h:371
TUtilities::CheckFileStringZeroDelimiter
bool CheckFileStringZeroDelimiter(std::ifstream &InFile)
checks that the value is a string ('0' only accepted as the delimiter), returns true for success
Definition: Utilities.cpp:384
DisplayUnit.h
TRailGraphics::Code_w
Graphics::TBitmap * Code_w
Definition: GraphicUnit.h:948
FailDerailed
@ FailDerailed
Definition: TrainUnit.h:41
TTrainController::SPADWarning
bool SPADWarning
Definition: TrainUnit.h:729
TTrain::ZeroPowerNoFrontSplitMessage
bool ZeroPowerNoFrontSplitMessage
Definition: TrainUnit.h:300
TRailGraphics::Code_s
Graphics::TBitmap * Code_s
Definition: GraphicUnit.h:944
TTrain::SaveOneSessionTrain
void SaveOneSessionTrain(int Caller, std::ofstream &OutFile)
Data for a single train is saved to a session file.
Definition: TrainUnit.cpp:6776
TTrain::DepartureTimeSet
bool DepartureTimeSet
set when stopped at a location and the next action is departure (set in UpdateTrain when ReleaseTime ...
Definition: TrainUnit.h:347
TTrain::Plotted
bool Plotted
set when train plotted on screen
Definition: TrainUnit.h:426
TTrain::LagElement
int LagElement
Definition: TrainUnit.h:327
TTrain::RemainHere
void RemainHere(int Caller)
Sends the 'train terminated' message to the performance log and sets TimetableFinished to true.
Definition: TrainUnit.cpp:5835
TTrainController::BufferAttentionWarning
bool BufferAttentionWarning
Definition: TrainUnit.h:729
TTrainController::TrainVectorAtIdent
TTrain & TrainVectorAtIdent(int Caller, int TrainID)
Return a reference to the train with ID TrainID, carries out validity checking on TrainID.
Definition: TrainUnit.cpp:8653
TPrefDirElement::GetRouteEXGraphicPtr
Graphics::TBitmap * GetRouteEXGraphicPtr()
Returns route graphic.
Definition: TrackUnit.h:307
TRailGraphics::smLightBlue
Graphics::TBitmap * smLightBlue
Definition: GraphicUnit.h:880
TTrainController::TContinuationAutoSigEntry::RouteNumber
int RouteNumber
the AllRoutesVector position of the route that the continuation is in
Definition: TrainUnit.h:657
TTrainController::StopTTClockMessage
void StopTTClockMessage(int Caller, AnsiString Message)
sends a message to the user and stops the timetable clock while it is displayed
Definition: TrainUnit.cpp:14030
TRailGraphics::CodeJ
Graphics::TBitmap * CodeJ
Definition: GraphicUnit.h:971
TUtilities::IncrementAnsiTimeOneMinute
AnsiString IncrementAnsiTimeOneMinute(AnsiString TimeVal)
takes "HH:MM" and increments it to "HH:MX", where MX == MM + 1, incrementing the hour if necessary
Definition: Utilities.cpp:550
TRailGraphics::CodeQ
Graphics::TBitmap * CodeQ
Definition: GraphicUnit.h:978
TDisplay::GetOutputLog9
TLabel * GetOutputLog9()
Definition: DisplayUnit.h:184
TAllRoutes::RemoveRouteElement
void RemoveRouteElement(int Caller, int HLoc, int VLoc, int ELink)
Erases the route element from Route2MultiMap and from the PrefDirVector.
Definition: TrackUnit.cpp:17065
TTrainController::CrashWarning
bool CrashWarning
Definition: TrainUnit.h:729
TTrainController::CheckLocationValidity
bool CheckLocationValidity(int Caller, AnsiString LocStr, bool GiveMessages, bool CheckLocationsExistInRailway)
Returns true if the location name complies with requirements.
Definition: TrainUnit.cpp:10161
TAllRoutes::TLockedRouteClass::LastXLinkPos
int LastXLinkPos
the XLinkPos value of the last (i.e. most forward) element in the route
Definition: TrackUnit.h:1508
TTrain::PlotTrain
void PlotTrain(int Caller, TDisplay *Disp)
Plots the train on the display in normal (zoomed-in) mode.
Definition: TrainUnit.cpp:7614
FailLevelCrossingCrash
@ FailLevelCrossingCrash
Definition: TrainUnit.h:43
TActionVectorEntry::DepartureTime
TDateTime DepartureTime
relevant times at which the action is timetabled, zeroed on creation so change to -1 as a marker for ...
Definition: TrainUnit.h:106
TRailGraphics::Code_f
Graphics::TBitmap * Code_f
Definition: GraphicUnit.h:931
FailCreateLockedRoute
@ FailCreateLockedRoute
Definition: TrainUnit.h:42
TTrainController::TContinuationTrainExpectationEntry::IncrementalDigits
int IncrementalDigits
Repeat headcode separation.
Definition: TrainUnit.h:679
TTrainController::SaveSessionTrains
void SaveSessionTrains(int Caller, std::ofstream &SessionFile)
save trains to a session file
Definition: TrainUnit.cpp:14045
TOneCompleteFormattedTrain::HeadCode
AnsiString HeadCode
Definition: TrainUnit.h:235
Intermediate
@ Intermediate
Definition: TrainUnit.h:71
TRailGraphics::Code_d
Graphics::TBitmap * Code_d
Definition: GraphicUnit.h:929
TTrain::JoinedBy
void JoinedBy(int Caller)
Carry out the actions needed when a train is waiting to be joined by another train.
Definition: TrainUnit.cpp:5664
FailIncorrectExit
@ FailIncorrectExit
Definition: TrainUnit.h:43
TRailGraphics::Code_t
Graphics::TBitmap * Code_t
Definition: GraphicUnit.h:945
TRailGraphics::ChangeBackgroundColour
void ChangeBackgroundColour(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewBackgroundColour, TColor OldBackgroundColour, bool &ColourError)
Definition: GraphicUnit.cpp:3398
TTrain::GetLeadElement
int GetLeadElement()
get LeadElement - used in RouteLockingRequired in TrackUnit.cpp
Definition: TrainUnit.h:620
TimeCmd
@ TimeCmd
Definition: TrainUnit.h:62
TTrain::BackgroundPtr
Graphics::TBitmap * BackgroundPtr[4]
the existing track graphic that the train headcode segment covers up (one for each headcode segment)
Definition: TrainUnit.h:448
TUtilities::LoadFileDouble
double LoadFileDouble(std::ifstream &InFile)
loads a double value from the file (converts from a string to a double) and uses the local decimal po...
Definition: Utilities.cpp:164
TTrain::DistanceToStationStop
int DistanceToStationStop
calculated in UpdateTrain & used in CalcDistanceToRedSignalandStopTime to cater for trains running ea...
Definition: TrainUnit.h:410
TTrainController::EntryPos
int EntryPos(int Caller, int TrainIDIn, int TrackVectorNumber)
Return the track entry link (Link[]) array position for the given train on track element at track vec...
Definition: TrainUnit.cpp:8615
SignallerLeave
@ SignallerLeave
Definition: TrainUnit.h:51
TTrain::PlotEntryPos
int PlotEntryPos[4]
the LinkPos value corresponding to the train entry link of the element where each of the 4 headcode c...
Definition: TrainUnit.h:441
NotStarted
@ NotStarted
Definition: TrainUnit.h:80
TTrack::OneNamedLocationElementAtLocation
bool OneNamedLocationElementAtLocation(int Caller, AnsiString LocationName)
True if there is at least one named location element with name 'LocationName', used in timetable inte...
Definition: TrackUnit.cpp:9790
TTrainController::SPADEvents
int SPADEvents
Definition: TrainUnit.h:776
TTrainController::LastTTTime
AnsiString LastTTTime
Stores the last time used in the timetable as an AnsiString - used for timetable analysis.
Definition: TrainUnit.h:725
TTrain::ZeroPowerNoRepeatShuttleMessage
bool ZeroPowerNoRepeatShuttleMessage
Definition: TrainUnit.h:307
TTrackElement::SigAspect
enum TTrackElement::@1 SigAspect
TRailGraphics::gl90set
Graphics::TBitmap * gl90set
Definition: GraphicUnit.h:704
TTrain::ActionVectorEntryPtr
TActionVectorEntry * ActionVectorEntryPtr
points to the current position in the ActionVector (a member of the TTrainDataEntry class)
Definition: TrainUnit.h:335
TTrainDataEntry
Contains all data for a single train.
Definition: TrainUnit.h:177
LeadMid
@ LeadMid
Definition: TrainUnit.h:268
FailMissedPass
@ FailMissedPass
Definition: TrainUnit.h:42
clSignalStopBackground
#define clSignalStopBackground
Definition: GraphicUnit.h:299
TTrainController::LateDeps
int LateDeps
Definition: TrainUnit.h:769
TAllRoutes::IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber
bool IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(int Caller, int TrackVectorPosition, int XLinkPos, TPrefDirElement &PrefDirElement, int &LockedVectorNumber)
Checks whether the preferred direction element at TrackVectorPosition with XLinkPos value is in a loc...
Definition: TrackUnit.cpp:17517
TrackUnit.h
TTrainController::CheckStartAllowable
bool CheckStartAllowable(int Caller, int RearPosition, int RearExitPos, AnsiString HeadCode, bool ReportFlag, TActionEventType &EventType)
Called when trying to introduce a new train - checks for points in correct orientation,...
Definition: TrainUnit.cpp:13059
TRailGraphics::Code_r
Graphics::TBitmap * Code_r
Definition: GraphicUnit.h:943
TTrain::SendMissedActionLogs
void SendMissedActionLogs(int Caller, int IncNum, TActionVectorEntry *Ptr)
Missed actions (see NameInTimetableBeforeCDT above) sent to the performance log and performance file.
Definition: TrainUnit.cpp:5856
TTrainController::TContinuationTrainExpectationEntry::RepeatNumber
int RepeatNumber
service RepeatNumber
Definition: TrainUnit.h:675
TRailGraphics::CodeX
Graphics::TBitmap * CodeX
Definition: GraphicUnit.h:985
TOneTrainFormattedEntry::Time
AnsiString Time
the time of the action as a string
Definition: TrainUnit.h:222
TRailGraphics::CodeT
Graphics::TBitmap * CodeT
Definition: GraphicUnit.h:981
TTrainDataEntry::PowerAtRail
double PowerAtRail
in Watts (taken as 80% of the train's Gross Power, i.e. that entered by the user)
Definition: TrainUnit.h:185
TTrainController::LastTrainLoaded
int LastTrainLoaded
displays last train loaded from session file, used for debugging
Definition: TrainUnit.h:786
TTrainController::NotStartedTrainLateMins
float NotStartedTrainLateMins
total late minutes of trains that haven't started yet on exit operation for locations not reached yet
Definition: TrainUnit.h:749
Utilities.h
TTrainController::TContinuationAutoSigVectorIterator
TContinuationAutoSigVector::iterator TContinuationAutoSigVectorIterator
Definition: TrainUnit.h:665
TTrain::TrainOnContinuation
bool TrainOnContinuation(int Caller)
Returns true if any part of train on a continuation - called when checking for failures,...
Definition: TrainUnit.cpp:7876
TTrainController::CheckSessionLockedRoutes
bool CheckSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
Part of the session file integrity check for locked routes, true for success.
Definition: TrainUnit.cpp:14149
FailTrainEntry
@ FailTrainEntry
Definition: TrainUnit.h:40
MidLag
@ MidLag
Definition: TrainUnit.h:268
TDisplay::Update
void Update()
Repaint the screen display.
Definition: DisplayUnit.h:221
TTrainController::LocServiceTimesDepTimeSort
bool LocServiceTimesDepTimeSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:822
TTrainController::CreateTTAnalysisFile
bool CreateTTAnalysisFile(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir, bool ArrChecked, bool DepChecked, bool AtLocChecked, int ArrRange, int DepRange)
Generate a timetable analysis file in the 'Formatted Timetables' folder, return false if failed for a...
Definition: TrainUnit.cpp:14991
TTrainController::CheckSessionTrains
bool CheckSessionTrains(int Caller, std::ifstream &InFile)
Part of the session file integrity check for train entries, true for success.
Definition: TrainUnit.cpp:14087
StartNew
@ StartNew
Definition: TrainUnit.h:62
TakeSignallerControl
@ TakeSignallerControl
Definition: TrainUnit.h:49
LeadMidLag
@ LeadMidLag
Definition: TrainUnit.h:268
TTrack::GapFlashGreen
TGraphicElement * GapFlashGreen
Definition: TrackUnit.h:693
TTrain::ZeroPowerNoRepeatShuttleOrNewServiceMessage
bool ZeroPowerNoRepeatShuttleOrNewServiceMessage
flags to indicate whether the respective message has been sent
Definition: TrainUnit.h:308
TrainFailure
@ TrainFailure
Definition: TrainUnit.h:50
TTrain::MinsDelayed
float MinsDelayed
new at v2.2.0 for operator time to act panel. Calculated in UpdateTrain
Definition: TrainUnit.h:400
TTrack::TSigElement::SigPtr
Graphics::TBitmap * SigPtr
pointer to the graphic
Definition: TrackUnit.h:627
TAllRoutes::FindRouteNumberFromRoute2MultiMapNoErrors
bool FindRouteNumberFromRoute2MultiMapNoErrors(int Caller, int HLoc, int VLoc, int ELink, int &RouteNumber)
If a route is present at H, V & Elink returns true with RouteNumber giving vector position in AllRout...
Definition: TrackUnit.cpp:16816
TTrainController::Last2CharactersBothDigits
bool Last2CharactersBothDigits(int Caller, AnsiString HeadCode)
Checks the last two characters in HeadCode and returns true if both are digits.
Definition: TrainUnit.cpp:9769
TRailGraphics::Code2
Graphics::TBitmap * Code2
Definition: GraphicUnit.h:954
TTrack::GetVectorPositionsFromInactiveTrackMap
TIMPair GetVectorPositionsFromInactiveTrackMap(int Caller, int HLoc, int VLoc, bool &FoundFlag)
Similar to GetVectorPositionFromTrackMap but for inactive elements, a pair is returned because there ...
Definition: TrackUnit.cpp:5195
TTrain::BackgroundColour
TColor BackgroundColour
the background colour of the train's headcode graphics
Definition: TrainUnit.h:455
TTrainOperatingData::EventReported
TActionEventType EventReported
Definition: TrainUnit.h:156
TTrain
Definition: TrainUnit.h:271
TRailGraphics::Code_z
Graphics::TBitmap * Code_z
Definition: GraphicUnit.h:951
TTrainController::CheckTimeValidity
bool CheckTimeValidity(int Caller, AnsiString TimeStr, TDateTime &Time)
returns true if the time complies with requirements
Definition: TrainUnit.cpp:9788
clSignallerStopped
#define clSignallerStopped
Definition: GraphicUnit.h:298
TTrackElement::TrainIDOnBridgeTrackPos23
int TrainIDOnBridgeTrackPos23
Set to the TrainID value when a train is present on the element, bridges can have two trains present ...
Definition: TrackUnit.h:151
TOnePrefDir::GetFixedPrefDirElementAt
const TPrefDirElement & GetFixedPrefDirElementAt(int Caller, int At) const
Return a non-modifiable element at PrefDirVector position 'At'.
Definition: TrackUnit.cpp:10398
FailBuffersPreventingStart
@ FailBuffersPreventingStart
Definition: TrainUnit.h:43
TTrainController::LocServiceTimesArrTimeSort
bool LocServiceTimesArrTimeSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:817
TOneTrainFormattedEntry::Action
AnsiString Action
includes location if relevant
Definition: TrainUnit.h:220
TTrainController::ContinuationTrainExpectationMultiMap
TContinuationTrainExpectationMultiMap ContinuationTrainExpectationMultiMap
Multimap for TContinuationTrainExpectationEntry objects, the access key is the expectation time.
Definition: TrainUnit.h:799
TTrain::StoppedForTrainInFront
bool StoppedForTrainInFront
Definition: TrainUnit.h:432
GapJump
@ GapJump
Definition: TrackUnit.h:63
TRailGraphics::CodeK
Graphics::TBitmap * CodeK
Definition: GraphicUnit.h:972
TTrainController::MissedStops
int MissedStops
Definition: TrainUnit.h:771
NoSequence
@ NoSequence
Definition: TrainUnit.h:71
clCallOnBackground
#define clCallOnBackground
Definition: GraphicUnit.h:292
TTrackElement::Length01
int Length01
Definition: TrackUnit.h:147
TTrackElement::SpeedLimit01
int SpeedLimit01
Definition: TrackUnit.h:147
TTrain::TerminatedMessageSent
bool TerminatedMessageSent
set when a 'train terminated' message has been logged, to prevent its being logged more than once
Definition: TrainUnit.h:367
Finish
@ Finish
Definition: TrainUnit.h:71
TTrain::FinishJoin
void FinishJoin(int Caller)
Carry out the actions needed when a train is waiting to join another train.
Definition: TrainUnit.cpp:5617
TTrainController::FinishedOperation
void FinishedOperation(int Caller)
called when exiting operation mode to delete all trains and timetable data etc
Definition: TrainUnit.cpp:8254
TTrain::MidElement
int MidElement
Definition: TrainUnit.h:327
TTrackElement::TempTrackMarker23
bool TempTrackMarker23
Utility markers for program use.
Definition: TrackUnit.h:137
TAllRoutes::CallonVector
std::vector< TCallonEntry > CallonVector
the store of all call-on entries
Definition: TrackUnit.h:1554
TTrain::LeadElement
int LeadElement
Definition: TrainUnit.h:327
clDerailedBackground
#define clDerailedBackground
Definition: GraphicUnit.h:294
TTrain::RepeatNumber
int RepeatNumber
indicates which of the repeating services this train represents (0 = first service)
Definition: TrainUnit.h:321
TTrainDataEntry::ActionVector
TActionVector ActionVector
all the actions for the train
Definition: TrainUnit.h:195
TTrainController::BaseTime
TDateTime BaseTime
CurrentDateTime (i.e. real time) when operation restarts after a pause.
Definition: TrainUnit.h:638
TTrain::StartSpeed
int StartSpeed
the speed of the train when introduced into the railway (in km/h)
Definition: TrainUnit.h:325
TTrainController::LoadSessionLockedRoutes
void LoadSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
load locked routes from a session file
Definition: TrainUnit.cpp:14128
TTrainController::EarlyPasses
int EarlyPasses
Definition: TrainUnit.h:766
TTrain::HeadCode
AnsiString HeadCode
needs own HeadCode because repeat entries will differ from TrainDataEntry.HeadCode
Definition: TrainUnit.h:290
TDisplay::GetOutputLog7
TLabel * GetOutputLog7()
Definition: DisplayUnit.h:174
TTrainController::TOpTimeToActMultiMapEntry
std::pair< float, THCandTrainPosParam > TOpTimeToActMultiMapEntry
Definition: TrainUnit.h:720
FailMissedSplit
@ FailMissedSplit
Definition: TrainUnit.h:41
TTrain::EntryTime
TDateTime EntryTime
Definition: TrainUnit.h:414
TExitListIterator
TExitList::iterator TExitListIterator
Definition: TrainUnit.h:88
TTrainController::TContinuationAutoSigEntry::FirstDelay
double FirstDelay
Definition: TrainUnit.h:653
TTrainController::SaveTrainDataVectorToFile
void SaveTrainDataVectorToFile(int Caller)
diagnostic function to store all train data to a file for examination, not used normally
Definition: TrainUnit.cpp:13852
TOnePrefDir::PrefDirSize
unsigned int PrefDirSize() const
Return the vector size.
Definition: TrackUnit.h:1275
TTrack::PlotSignal
void PlotSignal(int Caller, TTrackElement TrackElement, TDisplay *Disp)
Plot signals on screen according to their aspect (Attribute value)
Definition: TrackUnit.cpp:5426
End
@ End
Definition: TrackUnit.h:73
TTrain::OneLengthAccelDecel
bool OneLengthAccelDecel
set when a train can only move forwards one element before stopping but needs to accelerate for the f...
Definition: TrainUnit.h:357
TTrain::FloatingLabelNextString
AnsiString FloatingLabelNextString(int Caller, TActionVectorEntry *Ptr)
Used in the floating window to display the 'Next' action.
Definition: TrainUnit.cpp:6416
TTrack::TimetabledLocationNameAllocated
bool TimetabledLocationNameAllocated(int Caller, AnsiString LocationName)
True if a non-empty LocationName found as a timetabled location name i.e. not as a continuation name.
Definition: TrackUnit.cpp:7965
TTrain::FailedTrainNoFinishJoinMessage
bool FailedTrainNoFinishJoinMessage
Definition: TrainUnit.h:302
TTrack::InactiveTrackElementAt
TTrackElement & InactiveTrackElementAt(int Caller, int At)
A range-checked version of InactiveTrackElement.at(At)
Definition: TrackUnit.cpp:9531
ExitRailway
@ ExitRailway
Definition: TrainUnit.h:63
TPrefDirElement::GetTrackVectorPosition
unsigned int GetTrackVectorPosition() const
Returns TrackVectorPosition.
Definition: TrackUnit.h:295
TTrain::StationStopCalculated
bool StationStopCalculated
used in calculating DistanceToStationStop for trains running early before they have reached the stop ...
Definition: TrainUnit.h:363
TTrainController::LoadSessionContinuationAutoSigEntries
void LoadSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
load ContinuationAutoSigEntries from a session file
Definition: TrainUnit.cpp:14211
TTrackElement
Basic track elements as implemented in the overall railway layout.
Definition: TrackUnit.h:123
TRailGraphics::Code_n
Graphics::TBitmap * Code_n
Definition: GraphicUnit.h:939
FailMissedNewService
@ FailMissedNewService
Definition: TrainUnit.h:42
TUtilities::Format96HHMMSS
AnsiString Format96HHMMSS(TDateTime DateTime)
formats a TDateTime into an AnsiString of the form hh:mm:ss where hh runs from 00 to 95 & resets when...
Definition: Utilities.cpp:520
TRailGraphics::smSolidBgnd
Graphics::TBitmap * smSolidBgnd
Definition: GraphicUnit.h:989
clTRSBackground
#define clTRSBackground
Definition: GraphicUnit.h:303
TTrainController::OtherMissedEvents
int OtherMissedEvents
Definition: TrainUnit.h:775
TTrainDataEntry::Description
AnsiString Description
headcode is the first train's headcode, rest are calculated from repeat information; ServiceReference...
Definition: TrainUnit.h:179
TTrain::ExitSpeedHalf
double ExitSpeedHalf
speed when half way into the next element
Definition: TrainUnit.h:379
SignalPost
@ SignalPost
Definition: TrackUnit.h:63
TRailGraphics::Code_h
Graphics::TBitmap * Code_h
Definition: GraphicUnit.h:933
TTrain::LastActionTime
TDateTime LastActionTime
time of the last timetabled action, used to ensure at least a 30 second delay before the next action
Definition: TrainUnit.h:418
TDisplay::GetOutputLog1
TLabel * GetOutputLog1()
Return pointers to warning message logs (appear above the railway display during operation)
Definition: DisplayUnit.h:143
TAllRoutes::TRoute2MultiMapIterator
TRoute2MultiMap::iterator TRoute2MultiMapIterator
Definition: TrackUnit.h:1531
TTrain::TrainFailurePending
bool TrainFailurePending
set when failure due & takes effect when all PlotElements properly set, added at v2....
Definition: TrainUnit.h:310
FailMissedTerminate
@ FailMissedTerminate
Definition: TrainUnit.h:42
TRailGraphics::Code9
Graphics::TBitmap * Code9
Definition: GraphicUnit.h:961
TTrainDataEntry::HeadCode
AnsiString HeadCode
Definition: TrainUnit.h:179
TTrain::TrainHasFailed
void TrainHasFailed(int Caller)
Called when there is a random train failure.
Definition: TrainUnit.cpp:5014
clNormalBackground
#define clNormalBackground
Definition: GraphicUnit.h:297
TTrainController::SecondPassActions
bool SecondPassActions(int Caller, bool GiveMessages)
Carry out further detailed timetable consistency checks, return true for success.
Definition: TrainUnit.cpp:10828
TActionVectorEntry::NonRepeatingShuttleLinkEntryPtr
TTrainDataEntry * NonRepeatingShuttleLinkEntryPtr
pointer used by shuttles for the non-shuttle train links, in & out, the corresponding non-shuttle lin...
Definition: TrainUnit.h:120
TTrainController::SPADRisks
int SPADRisks
Definition: TrainUnit.h:777
TTrainController::TLocServiceTimesVector
std::vector< TLocServiceTimes > TLocServiceTimesVector
Definition: TrainUnit.h:706
TTrainController::LocServiceTimesAtLocTimeSort
bool LocServiceTimesAtLocTimeSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:826
TTrain::EntrySpeed
double EntrySpeed
speed at which the train enters the next element
Definition: TrainUnit.h:377
TConfiguration
TConfiguration
< describes the type of track link. 'End' is used for both buffer stop and continuation entry/exit po...
Definition: TrackUnit.h:72
TTrain::TrainGone
bool TrainGone
set when train has left the railway, so it can be removed from the display at the next clock tick
Definition: TrainUnit.h:428
TTrackType
TTrackType
< describes the type of track element
Definition: TrackUnit.h:62
TTrainController::OnTimePasses
int OnTimePasses
Definition: TrainUnit.h:774
TTrainController::SigSHigh
bool SigSHigh
Definition: TrainUnit.h:743
TRailGraphics::smCaramel
Graphics::TBitmap * smCaramel
Definition: GraphicUnit.h:878
TGraphicElement::PlotOriginal
void PlotOriginal(int Caller, TDisplay *Disp)
Plot the original graphic on screen.
Definition: TrackUnit.cpp:1597
clBufferAttentionNeeded
#define clBufferAttentionNeeded
Definition: GraphicUnit.h:290
TTrain::StoppedAfterSPAD
bool StoppedAfterSPAD
Definition: TrainUnit.h:432
FailSPAD
@ FailSPAD
Definition: TrainUnit.h:40
TTrainController::OnTimeArrivals
int OnTimeArrivals
Definition: TrainUnit.h:772
clTrainFailedBackground
#define clTrainFailedBackground
Definition: GraphicUnit.h:304
TRailGraphics::Code_x
Graphics::TBitmap * Code_x
Definition: GraphicUnit.h:949
clFrontCodeSignaller
#define clFrontCodeSignaller
Definition: GraphicUnit.h:295
TimeCmdHeadCode
@ TimeCmdHeadCode
Definition: TrainUnit.h:62
Utilities
TUtilities * Utilities
Definition: Utilities.cpp:47
TFixedTrackPiece::SmallGraphicPtr
Graphics::TBitmap * SmallGraphicPtr
the track bitmap for display on the zoomed-out railway
Definition: TrackUnit.h:92
TActionVectorEntry::ArrivalTime
TDateTime ArrivalTime
Definition: TrainUnit.h:106
TTrain::StoppedAtBuffers
bool StoppedAtBuffers
Definition: TrainUnit.h:432
TTrainController::SplitEntry
bool SplitEntry(int Caller, AnsiString OneEntry, bool GiveMessages, bool CheckLocationsExistInRailway, AnsiString &First, AnsiString &Second, AnsiString &Third, AnsiString &Fourth, int &RearStartOrRepeatMins, int &FrontStartPosition, TTimetableFormatType &TimetableFormatType, TTimetableLocationType &LocationType, TTimetableSequenceType &SequenceType, TTimetableShuttleLinkType &ShuttleLinkType, TExitList &ExitList, bool &Warning)
Parse a single timetable service action, return true for success.
Definition: TrainUnit.cpp:9839
TRailGraphics::Code_b
Graphics::TBitmap * Code_b
Definition: GraphicUnit.h:927
TTrain::Mass
int Mass
in kg
Definition: TrainUnit.h:408
TRailGraphics::Code0
Graphics::TBitmap * Code0
Definition: GraphicUnit.h:952
TTrainController::ProcessOneTimetableLine
bool ProcessOneTimetableLine(int Caller, int Count, AnsiString OneLine, bool &EndOfFile, bool FinalCall, bool GiveMessages, bool CheckLocationsExistInRailway)
Carry out preliminary (mainly syntax) validity checks on a single timetable service entry and (if Fin...
Definition: TrainUnit.cpp:9138
TTrainController::TContinuationTrainExpectationEntry::IncrementalMinutes
int IncrementalMinutes
Repeat separation in minutes.
Definition: TrainUnit.h:677
TTrain::LeadExitPos
int LeadExitPos
Definition: TrainUnit.h:327
TTrain::FrontElementLength
int FrontElementLength
values associated with the element immediately in front of the train (speed in km/h,...
Definition: TrainUnit.h:406
TTimetableShuttleLinkType
TTimetableShuttleLinkType
Definition: TrainUnit.h:74
TRailGraphics::CodeP
Graphics::TBitmap * CodeP
Definition: GraphicUnit.h:977
Pass
@ Pass
Definition: TrainUnit.h:51
TTrainController::DerailWarning
bool DerailWarning
Definition: TrainUnit.h:729
TTrainController::TContinuationTrainExpectationMultiMapPair
std::pair< TDateTime, TContinuationTrainExpectationEntry > TContinuationTrainExpectationMultiMapPair
a single multimap entry
Definition: TrainUnit.h:691
RouteForceCancelled
@ RouteForceCancelled
Definition: TrainUnit.h:44
EnRoute
@ EnRoute
Definition: TrainUnit.h:67
TTrain::IsTrainIDOnBridgeTrackPos23
bool IsTrainIDOnBridgeTrackPos23(int Caller, unsigned int TrackVectorPosition)
True if train is on a bridge on trackpos 2 & 3.
Definition: TrainUnit.cpp:2881
TTrack::DiagonalFouledByTrain
bool DiagonalFouledByTrain(int Caller, int HLoc, int VLoc, int DiagonalLinkNumber, int &TrainID)
As DiagonalFouledByRouteOrTrain (in TAllRoutes) but only checks for a train (may or may not be a rout...
Definition: TrackUnit.cpp:10211
TTrainController::TotLateDepMins
float TotLateDepMins
Definition: TrainUnit.h:759
TDisplay::ZoomOutFlag
bool ZoomOutFlag
true when zoomed-out
Definition: DisplayUnit.h:69
TRailGraphics::CodeC
Graphics::TBitmap * CodeC
Definition: GraphicUnit.h:964
TTrain::PlotElement
int PlotElement[4]
the TrackVectorPosition of the element where each of the 4 headcode characters is plotted (need to be...
Definition: TrainUnit.h:439
TTrainController::TServiceCallingLocsList
std::list< AnsiString > TServiceCallingLocsList
Used in determining train directions in timetable conflict analysis.
Definition: TrainUnit.h:709
TTrainController::TrainVector
TTrainVector TrainVector
vector containing all trains currently in the railway
Definition: TrainUnit.h:807
TTrack::ResetAllTrainIDElements
void ResetAllTrainIDElements(int Caller)
Track elements have members that indicates whether and on what track a train is present (TrainIDOnEle...
Definition: TrackUnit.cpp:6845
TExitList
std::list< int > TExitList
a list of valid train exit TrackVector positions for 'Fer' entries
Definition: TrainUnit.h:84
TRailGraphics::Code_l
Graphics::TBitmap * Code_l
Definition: GraphicUnit.h:937
TTrainController::TContinuationAutoSigEntry
< TTClockTime when last session saved - to prevent display of warning message on exit session if < 5 ...
Definition: TrainUnit.h:651
TUtilities::CallLogPop
void CallLogPop(int Caller)
pops the last entry off the call stack, throws an error if called when empty
Definition: Utilities.cpp:50
TTrain::IncrementalMinutes
int IncrementalMinutes
the number of minutes to increment by in repeat entries
Definition: TrainUnit.h:315
TTrain::MaximumSpeedLimit
static const int MaximumSpeedLimit
km/h
Definition: TrainUnit.h:284
TRailGraphics::CodeI
Graphics::TBitmap * CodeI
Definition: GraphicUnit.h:970
TTrainController::TotEarlyArrMins
float TotEarlyArrMins
values for performance file summary
Definition: TrainUnit.h:755
TTrain::WriteTrainToImage
void WriteTrainToImage(int Caller, Graphics::TBitmap *Bitmap)
Called by TTrainController::WriteTrainsToImage (called by TInterface::SaveOperatingImage1Click) to ad...
Definition: TrainUnit.cpp:7626
TRailGraphics::CodeL
Graphics::TBitmap * CodeL
Definition: GraphicUnit.h:973
TTrainDataEntry::MaxBrakeRate
double MaxBrakeRate
in metres/sec/sec
Definition: TrainUnit.h:181
FNSNonRepeatToShuttle
@ FNSNonRepeatToShuttle
Definition: TrainUnit.h:62
TDisplay::PlotOutput
void PlotOutput(int Caller, int HPos, int VPos, Graphics::TBitmap *PlotItem)
Plot the graphic at screen position HPos & VPos.
Definition: DisplayUnit.cpp:85
TUtilities::SaveFileBool
void SaveFileBool(std::ofstream &OutFile, bool SaveBool)
stores '1' if the bool is true or '0' if false to the file, then a CR
Definition: Utilities.cpp:108
TTrainDataEntry::SignallerSpeed
int SignallerSpeed
in km/h for use when under signaller control
Definition: TrainUnit.h:191
TTrain::AbleToMove
bool AbleToMove(int Caller)
Indicates that a train is not prevented from moving - used to allow appropriate popup menu options wh...
Definition: TrainUnit.cpp:6281
TTrainController::MTBFHours
double MTBFHours
Mean time between failures in timetable clock hours.
Definition: TrainUnit.h:746
TOneRoute::ForceCancelRoute
void ForceCancelRoute(int Caller)
Cancel a route immediately if a train occupies it when travelling in the wrong direction (or occupies...
Definition: TrackUnit.cpp:15997
TTrain::TrainMode
TTrainMode TrainMode
mode of operation - either Timetable (running under timetable control) or Signaller (running under si...
Definition: TrainUnit.h:420
TTrainController::CheckNonRepeatingShuttleLinkTime
bool CheckNonRepeatingShuttleLinkTime(int Caller, TDateTime ReverseEventTime, TDateTime ForwardEventTime, int RepeatMins, int RepeatNumber)
The forward train is the finish shuttle entry 'Fns-sh', the reverse (new non-repeating service) time ...
Definition: TrainUnit.cpp:13541
TTrack::ThisNamedLocationLongEnoughForSplit
bool ThisNamedLocationLongEnoughForSplit(int Caller, AnsiString LocationName, int FirstNamedElementPos, int &SecondNamedElementPos, int &FirstNamedLinkedElementPos, int &SecondNamedLinkedElementPos)
See above under 'OneNamedLocationLongEnoughForSplit'.
Definition: TrackUnit.cpp:9663
TUtilities::Format96HHMM
AnsiString Format96HHMM(TDateTime DateTime)
formats a TDateTime into an AnsiString of the form hh:mm where hh runs from 00 to 95 & resets when it...
Definition: Utilities.cpp:535
TTrain::CallOnMaxSpeed
static const int CallOnMaxSpeed
km/h
Definition: TrainUnit.h:278
TTrain::CheckAndCancelRouteForWrongEndEntry
void CheckAndCancelRouteForWrongEndEntry(int Caller, int Element, int EntryPos)
Checks whether Element and EntryPos (where train is about to enter) is on an existing route (or cross...
Definition: TrainUnit.cpp:3077
TRailGraphics::CodeW
Graphics::TBitmap * CodeW
Definition: GraphicUnit.h:984
TTrain::ReleaseTime
TDateTime ReleaseTime
Definition: TrainUnit.h:416
TTrain::GetTrainHeadCode
AnsiString GetTrainHeadCode(int Caller)
Returns the train headcode, taking account of the RepeatNumber.
Definition: TrainUnit.cpp:4689
TTrain::LowEntryValue
bool LowEntryValue(int EntryLink) const
Returns true if EntryLink is 1, 2, 4 or 7, in these circumstances the front of the train (i....
Definition: TrainUnit.cpp:2426
RearSplit
@ RearSplit
Definition: TrainUnit.h:49
TActionVectorEntry::FrontStartOrRepeatDigits
int FrontStartOrRepeatDigits
dual-purpose variables used for the TrackVectorPositions of the rear and front train starting element...
Definition: TrainUnit.h:104
SignallerControlStop
@ SignallerControlStop
Definition: TrainUnit.h:51
TTrack::TrackVector
TTrackVector TrackVector
Definition: TrackUnit.h:710
TTrain::HasTrainGone
bool HasTrainGone()
Check whether the train has left the railway, so that it can be removed from the display at the next ...
Definition: TrainUnit.h:604
TRailGraphics::Code_i
Graphics::TBitmap * Code_i
Definition: GraphicUnit.h:934
TTrainController::ExcessLCDownMins
float ExcessLCDownMins
total excess time in minutes over the 3 minutes barriers down allowance for level crossings
Definition: TrainUnit.h:753
TRailGraphics::Code5
Graphics::TBitmap * Code5
Definition: GraphicUnit.h:957
TTrackElement::CallingOnSet
bool CallingOnSet
Used for for signals only when a train is being called on - used to plot the position lights.
Definition: TrackUnit.h:133
TUtilities::SaveFileInt
void SaveFileInt(std::ofstream &OutFile, int SaveInt)
stores the int value to the file, then a CR
Definition: Utilities.cpp:117
TrainController
TTrainController * TrainController
the object pointer, one object only - created in InterfaceUnit
Definition: TrainUnit.cpp:54
TTrack::OneNamedLocationLongEnoughForSplit
bool OneNamedLocationLongEnoughForSplit(int Caller, AnsiString LocationName)
Definition: TrackUnit.cpp:9566
TTrain::BufferAtExit
bool BufferAtExit(int Caller, int Element, int Exitpos) const
True if Element is a buffer and Exitpos is the buffer end.
Definition: TrainUnit.cpp:2813
TTrain::IsTrainTerminating
bool IsTrainTerminating(int Caller)
True if train service terminates at its current location.
Definition: TrainUnit.cpp:6253
TTrainController::SecondPassMessage
void SecondPassMessage(bool GiveMessages, AnsiString Message)
Give a user message during timetable integrity checking if GiveMessages is true, ignore if false.
Definition: TrainUnit.cpp:13634
SignallerStepForward
@ SignallerStepForward
Definition: TrainUnit.h:51
TTrack::GetHLocMin
int GetHLocMin()
Definition: TrackUnit.h:775
TTrainController::CheckCrossReferencesAndSetData
bool CheckCrossReferencesAndSetData(int Caller, AnsiString SoughtHeadCode, AnsiString SeekingHeadCode, bool Shuttle, bool GiveMessages)
A timetable validation function where all service cross references are checked for validity and set p...
Definition: TrainUnit.cpp:12355
TDisplay::GetOutputLog5
TLabel * GetOutputLog5()
Definition: DisplayUnit.h:164
TTrainController::OpTimeToActUpdateCounter
unsigned int OpTimeToActUpdateCounter
new v2.2.0, incremented in Interface.cpp, controls updating for OpTimeToActPanel
Definition: TrainUnit.h:791
TTrain::BeingCalledOn
bool BeingCalledOn
in course of being called on to a station
Definition: TrainUnit.h:341
TUtilities::CheckFileInt
bool CheckFileInt(std::ifstream &InFile, int Lowest, int Highest)
checks that the value is an int lying between Lowest & Highest (inclusive), returns true for success
Definition: Utilities.cpp:217
TRailGraphics::ChangeForegroundColour2
void ChangeForegroundColour2(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewForegroundColour, TColor BackgroundColour)
New function to do the same as the above but with fewer pixel changes - for use in LoadSession to avo...
Definition: GraphicUnit.cpp:3348
TRailGraphics::Code_e
Graphics::TBitmap * Code_e
Definition: GraphicUnit.h:930
TTrain::HoldAtLocationInTTMode
bool HoldAtLocationInTTMode
true if actions are needed before train departs
Definition: TrainUnit.h:292
TTrain::PlotTrainInZoomOutMode
void PlotTrainInZoomOutMode(int Caller, bool Flash)
Plots the train on screen in zoomed-out mode, state of 'Flash' determines whether the flashing trains...
Definition: TrainUnit.cpp:7435
TTrain::LoadOneSessionTrain
void LoadOneSessionTrain(int Caller, std::ifstream &InFile)
Create one train with relevant member values from the sesion file.
Definition: TrainUnit.cpp:6937
TTrain::PlotBackgroundGraphic
void PlotBackgroundGraphic(int Caller, int ArrayNumber, TDisplay *Disp) const
Replot the graphic pointed to by BackgroundPtr (see above) after a train has passed.
Definition: TrainUnit.cpp:2805
TTrainController::TotLateArrMins
float TotLateArrMins
Definition: TrainUnit.h:758
TTrain::HOffset
int HOffset[4]
Definition: TrainUnit.h:435
FailMissedArrival
@ FailMissedArrival
Definition: TrainUnit.h:41
TTrain::UpdateCounter
unsigned int UpdateCounter
used in train splitting operations to prevent too frequent checks for a location being long enough fo...
Definition: TrainUnit.h:412
NewService
@ NewService
Definition: TrainUnit.h:49
FailEnterLockedRoute
@ FailEnterLockedRoute
Definition: TrainUnit.h:42
TTrainController::PlotAllTrainsInZoomOutMode
void PlotAllTrainsInZoomOutMode(int Caller, bool Flash)
Plots all trains on screen in zoomed-out mode, state of 'Flash' determines whether the flashing train...
Definition: TrainUnit.cpp:14364
TOneRoute
A descendent of TOnePrefDir used for routes. Used during contruction of a route (ConstructRoute) and ...
Definition: TrackUnit.h:1371
TTrain::SignallerStopped
bool SignallerStopped
Definition: TrainUnit.h:432
TTrain::BufferZoomOutFlashRequired
bool BufferZoomOutFlashRequired
set when train is at buffers and is to flash in zoomout mode (i.e. when reaches buffers unexpectedly ...
Definition: TrainUnit.h:343
TAllRoutes::TRouteType
TRouteType
Definition: TrackUnit.h:1515
TTrain::TrainDataEntryPtr
TTrainDataEntry * TrainDataEntryPtr
points to the current position in the timetable's TrainDataVector
Definition: TrainUnit.h:333
TTrainDataEntry::StartSpeed
int StartSpeed
in km/h
Definition: TrainUnit.h:193
TTrainDataEntry::NumberOfTrains
int NumberOfTrains
number of repeats + 1
Definition: TrainUnit.h:189
TTrainController::Derailments
int Derailments
Definition: TrainUnit.h:764
clStationStopBackground
#define clStationStopBackground
Definition: GraphicUnit.h:301
TDisplay::GetOutputLog6
TLabel * GetOutputLog6()
Definition: DisplayUnit.h:169
ShuttleLink
@ ShuttleLink
Definition: TrainUnit.h:75
TRailGraphics::CodeM
Graphics::TBitmap * CodeM
Definition: GraphicUnit.h:974
Crossover
@ Crossover
Definition: TrackUnit.h:63
TTrain::ClearToNextSignal
bool ClearToNextSignal(int Caller)
Checks forward from train LeadElement, following leading point attributes but ignoring trailing point...
Definition: TrainUnit.cpp:4332
TTrainController::TContinuationTrainExpectationEntry::Description
AnsiString Description
service description
Definition: TrainUnit.h:671
AtLocation
@ AtLocation
Definition: TrainUnit.h:67
Signal
@ Signal
Definition: TrackUnit.h:73
TRailGraphics::Code_k
Graphics::TBitmap * Code_k
Definition: GraphicUnit.h:936
TRailGraphics::CodeF
Graphics::TBitmap * CodeF
Definition: GraphicUnit.h:967
TTrack::ContinuationNameMap
std::map< AnsiString, char > ContinuationNameMap
map of all continuation names, char is a dummy
Definition: TrackUnit.h:682
TTrackElement::TrainIDOnBridgeTrackPos01
int TrainIDOnBridgeTrackPos01
Definition: TrackUnit.h:151
TAllRoutes::GetRouteTypeAndNumber
TRouteType GetRouteTypeAndNumber(int Caller, int TrackVectorPosition, int LinkPos, int &RouteNumber)
Examines Route2MultiMap and if the element at TrackVectorPosition with LinkPos (can be entry or exit)...
Definition: TrackUnit.cpp:16506
TTrainController::TrainDataVector
TTrainDataVector TrainDataVector
vector containing the internal timetable
Definition: TrainUnit.h:805
TTrainController::ConsolidateSARNTAtLoc
AnsiString ConsolidateSARNTAtLoc(int Caller, const AnsiString Input, int &NumTrainsAtLoc)
Removes duplicates from and sorts ServiceAndRepeatNumTotal into alphabetical order for AtLoc listing ...
Definition: TrainUnit.cpp:16541
TAllRoutes::NotAutoSigsRoute
@ NotAutoSigsRoute
Definition: TrackUnit.h:1516
TTrain::ExitTimeHalf
TDateTime ExitTimeHalf
Definition: TrainUnit.h:414
Exited
@ Exited
Definition: TrainUnit.h:80
TTrack::TrackElementAt
TTrackElement & TrackElementAt(int Caller, int At)
A range-checked version of TrackElement.at(At)
Definition: TrackUnit.cpp:9518
TTrainController::LoadSessionTrains
void LoadSessionTrains(int Caller, std::ifstream &SessionFile)
load trains from a session file
Definition: TrainUnit.cpp:14061
TTrain::CheckOneSessionTrain
static bool CheckOneSessionTrain(std::ifstream &InFile)
Carries out an integrity check for the train section of a session file, if fails a message is given a...
Definition: TrainUnit.cpp:7169
TAllRoutes::GetRouteTypeAndGraphics
TRouteType GetRouteTypeAndGraphics(int Caller, int TrackVectorPosition, int LinkPos, Graphics::TBitmap *&EXGraphicPtr, Graphics::TBitmap *&EntryDirectionGraphicPtr)
Examines Route2MultiMap for the element at TrackVectorPosition with LinkPos (can be entry or exit).
Definition: TrackUnit.cpp:16331
TRailGraphics::CodeY
Graphics::TBitmap * CodeY
Definition: GraphicUnit.h:986
TTrain::SetOneGraphicCode
Graphics::TBitmap * SetOneGraphicCode(char CodeChar)
Return a pointer to the graphic corresponding to the character 'CodeVhar'.
Definition: TrainUnit.cpp:2062
TTrain::DerailPending
bool DerailPending
Definition: TrainUnit.h:432
TTrainMode
TTrainMode
indicates train operating mode, 'None' for not in use
Definition: TrainUnit.h:55
TAllRoutes::TCallonEntry
Used to store relevant values when a call-on found, ready for plotting an unrestricted route.
Definition: TrackUnit.h:1536
TRailGraphics::Code_a
Graphics::TBitmap * Code_a
Definition: GraphicUnit.h:926
TTrain::StoppedAtLocation
bool StoppedAtLocation
Definition: TrainUnit.h:432
TTrainController::TContinuationTrainExpectationMultiMapIterator
TContinuationTrainExpectationMultiMap::iterator TContinuationTrainExpectationMultiMapIterator
iterator for the multimap
Definition: TrainUnit.h:689
TTrain::MaximumMassLimit
static const int MaximumMassLimit
kg (i.e. 10,000 tonnes)
Definition: TrainUnit.h:280
TTrainController::StopTTClockFlag
bool StopTTClockFlag
when true the timetable clock is stopped, used for messages display and train popup menu display etc
Definition: TrainUnit.h:731
FrontSplit
@ FrontSplit
Definition: TrainUnit.h:49
TTrain::MaxExitSpeed
double MaxExitSpeed
the maximum speed that the train can exit the next element
Definition: TrainUnit.h:387
TTrainController::BFHigh
bool BFHigh
Definition: TrainUnit.h:743
TRailGraphics::Code4
Graphics::TBitmap * Code4
Definition: GraphicUnit.h:956
TRailGraphics::Code_g
Graphics::TBitmap * Code_g
Definition: GraphicUnit.h:932
TTrain::LagEntryPos
int LagEntryPos
Definition: TrainUnit.h:327
SNTShuttle
@ SNTShuttle
Definition: TrainUnit.h:62
TRailGraphics::CodeG
Graphics::TBitmap * CodeG
Definition: GraphicUnit.h:968
Leave
@ Leave
Definition: TrainUnit.h:49
TTrainController::UnexpectedExits
int UnexpectedExits
Definition: TrainUnit.h:779
TTrainController::TLocServiceTimes::AtLocTime
AnsiString AtLocTime
Definition: TrainUnit.h:701
TTrainController::ServiceReference
AnsiString ServiceReference
String used to display the offending service in timetable error messages.
Definition: TrainUnit.h:727
TTrain::FirstLaterStopRecoverableTime
float FirstLaterStopRecoverableTime
this used to deduct from RecoverableTime when arrive at a location for OperatorActionpanel
Definition: TrainUnit.h:404
TTrain::LeavingUnderSigControlAtContinuation
bool LeavingUnderSigControlAtContinuation
set when the train has reached an exit continuation when under signaller control, used to prevent the...
Definition: TrainUnit.h:355
TTrain::PlotTrainGraphic
void PlotTrainGraphic(int Caller, int ArrayNumber, TDisplay *Disp)
Plot the train's headcode character corresponding to ArrayNumber.
Definition: TrainUnit.cpp:2786
TRailGraphics::Code_j
Graphics::TBitmap * Code_j
Definition: GraphicUnit.h:935
NoFormat
@ NoFormat
Definition: TrainUnit.h:62
TTrainController::TwoOrMoreLocationsWarningGiven
bool TwoOrMoreLocationsWarningGiven
new at v2.6.0 to allow loops
Definition: TrainUnit.h:739
FailBufferCrash
@ FailBufferCrash
Definition: TrainUnit.h:43
WaitingForFJO
@ WaitingForFJO
Definition: TrainUnit.h:43
FailMissedExitRailway
@ FailMissedExitRailway
Definition: TrainUnit.h:42
TimeTimeLoc
@ TimeTimeLoc
Definition: TrainUnit.h:62
TTrain::GetOffsetValues
void GetOffsetValues(int Caller, int &HOffset, int &VOffset, int Link) const
Sets HOffset & VOffset (see above) for a single headcode character depending on the Link value.
Definition: TrainUnit.cpp:2356
TTrain::Derailed
bool Derailed
Definition: TrainUnit.h:432
TRailGraphics::ChangeSpecificColour
void ChangeSpecificColour(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor ColourToBeChanged, TColor NewColour)
Definition: GraphicUnit.cpp:3377
Enter
@ Enter
Definition: TrainUnit.h:49
TRailGraphics::bm93set
Graphics::TBitmap * bm93set
Definition: GraphicUnit.h:513
TTrack::GetTrackVectorPositionFromString
int GetTrackVectorPositionFromString(int Caller, AnsiString String, bool GiveMessages)
Takes the ElementID value (an AnsiString) (e.g. "8-13", "N43-N127", etc) and returns the correspondin...
Definition: TrackUnit.cpp:7050
TRailGraphics::Code_q
Graphics::TBitmap * Code_q
Definition: GraphicUnit.h:942
TTrain::NewTrainService
void NewTrainService(int Caller)
Carry out the actions needed when a train forms a new service (code Fns)
Definition: TrainUnit.cpp:5797
TTrainController::SaveSessionLockedRoutes
void SaveSessionLockedRoutes(int Caller, std::ofstream &SessionFile)
save locked routes to a session file
Definition: TrainUnit.cpp:14111
TTrain::OriginalPowerAtRail
double OriginalPowerAtRail
new at v2.4.0 to store value before a failure so it can be restored from here when repaired
Definition: TrainUnit.h:398
TTrainFormattedInformation
Contains all information for a single timetable entry for use in the formatted timetable.
Definition: TrainUnit.h:246
TTrainController::IsSNTEntryLocated
bool IsSNTEntryLocated(int Caller, const TTrainDataEntry &TDEntry, AnsiString &LocationName)
New trains introduced with 'Snt' may be at a timetabled location or elsewhere. This function checks a...
Definition: TrainUnit.cpp:12894
TTrainController::TContinuationTrainExpectationEntry::VectorPosition
int VectorPosition
TrackVectorPosition for the continuation element.
Definition: TrainUnit.h:681
TTrainController::TTrainController
TTrainController()
Constructor.
Definition: TrainUnit.cpp:7911
Timetable
@ Timetable
Definition: TrainUnit.h:56
TUtilities::SaveFileDouble
void SaveFileDouble(std::ofstream &OutFile, double SaveDouble)
converts the double value to a string (if double stored directly it is truncated to 6 digits) then st...
Definition: Utilities.cpp:123
TTrainController::GetRepeatHeadCode
AnsiString GetRepeatHeadCode(int Caller, AnsiString BaseHeadCode, int RepeatNumber, int IncDigits)
Return the service headcode for the repeat service.
Definition: TrainUnit.cpp:13220
TTrain::AbleToMoveButForSignal
bool AbleToMoveButForSignal(int Caller)
Indicates that a train is only prevented from moving by a signal - used to allow appropriate popup me...
Definition: TrainUnit.cpp:6333
TTrainController::OpActionPanelVisible
bool OpActionPanelVisible
new v2.2.0 flag to prevent time to act functions when not visible
Definition: TrainUnit.h:737
TTrack::GetAnyElementOppositeLinkPos
int GetAnyElementOppositeLinkPos(int Caller, int TrackVectorPosition, int LinkPos, bool &Derail)
Return the opposite link position for the element at TrackVectorPosition with link position LinkPos,...
Definition: TrackUnit.cpp:10065
TTrack::GapFlashRedPosition
int GapFlashRedPosition
TrackVectorPosition of the gap element that is flashing green or red.
Definition: TrackUnit.h:673
TActionVectorEntry::NumberOfRepeats
int NumberOfRepeats
the number of repeating services
Definition: TrainUnit.h:102
TAllRoutes::GetFixedRouteAt
const TOneRoute & GetFixedRouteAt(int Caller, int At) const
Returns a constant reference to the route at AllRoutesVector position 'At', after performing range ch...
Definition: TrackUnit.cpp:16176
TUtilities::clTransparent
TColor clTransparent
the display background colour, can be white, black or dark blue
Definition: Utilities.h:65
TTrainController::SignalStopWarning
bool SignalStopWarning
Definition: TrainUnit.h:729
TPrefDirElement::GetELinkPos
int GetELinkPos() const
Returns the ELink array position.
Definition: TrackUnit.h:277
TTrainController::CalcOperatingAndNotStartedTrainLateness
void CalcOperatingAndNotStartedTrainLateness(int Caller)
calculates additional lateness values for trains that haven't reached their destinations yet
Definition: TrainUnit.cpp:17282
TDisplay::PerformanceLog
void PerformanceLog(int Caller, AnsiString Statement)
Send Statement to the performance log on screen and to the file.
Definition: DisplayUnit.cpp:447
ShuttleLinkTypeForRepeatEntry
@ ShuttleLinkTypeForRepeatEntry
Definition: TrainUnit.h:75
TTrain::SignallerStopBrakeRate
double SignallerStopBrakeRate
the train brake rate when stopping under signaller control
Definition: TrainUnit.h:393
TTrainController::TLocServiceTimes::Location
AnsiString Location
Definition: TrainUnit.h:699
TOneCompleteFormattedTrain
A single train with its headcode + list of actions for use in the formatted timetable.
Definition: TrainUnit.h:233
TTrainController::SignallerTrainRemovedOnAutoSigsRoute
bool SignallerTrainRemovedOnAutoSigsRoute
true if train was on an AutoSigsRoute when removed by the signaller
Definition: TrainUnit.h:735
Terminate
@ Terminate
Definition: TrainUnit.h:49
TTrainController::MRSLow
bool MRSLow
Definition: TrainUnit.h:743
TTrack::ActiveTrackElementNameMap
TActiveTrackElementNameMap ActiveTrackElementNameMap
map of active track element names
Definition: TrackUnit.h:685
TTrain::Crashed
bool Crashed
Definition: TrainUnit.h:432
TTrackElement::HLoc
int HLoc
Definition: TrackUnit.h:145
TTrain::HeadCodePosition
Graphics::TBitmap * HeadCodePosition[4]
Set from the HeadCodeGrPtr[4] pointer values, HeadCodePosition[0] is always the front,...
Definition: TrainUnit.h:446
TTrain::TrainID
int TrainID
the train's identification number
Definition: TrainUnit.h:330
Running
@ Running
Definition: TrainUnit.h:80
TAllFormattedTrains
std::vector< TTrainFormattedInformation > TAllFormattedTrains
vector of all timetabled trains for use in the formatted timetable
Definition: TrainUnit.h:255
TRailGraphics::ChangeBackgroundColour3
void ChangeBackgroundColour3(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewBackgroundColour, TColor OldBackgroundColour)
as above but uses Scanline
Definition: GraphicUnit.cpp:3468
TRailGraphics::smCyan
Graphics::TBitmap * smCyan
Definition: GraphicUnit.h:879
TTrainController::MovingSuccessor
bool MovingSuccessor(const TActionVectorEntry &AVEntry)
A shorthand function that returns true if the successor to a given timetable action command should be...
Definition: TrainUnit.cpp:12222
TTrainController::LogActionError
void LogActionError(int Caller, AnsiString HeadCode, AnsiString OtherHeadCode, TActionEventType ActionEventType, AnsiString LocationID)
Send an error message to the performance log and file, and as a warning if appropriate.
Definition: TrainUnit.cpp:13645
TTrainController::CalcDistanceToRedSignalandStopTime
int CalcDistanceToRedSignalandStopTime(int Caller, int TrackVectorPosition, int TrackVectorPositionEntryPos, bool SigControlAndCanPassRedSignal, TActionVectorEntry *AVPtr, AnsiString HeadCode, int TrainID, float &CurrentStopTime, float &LaterStopTime, float &RecoverableTime, int &AvTrackSpeed)
new v2.2.0, calcs distance to red signal, returns -1 for no signal found, for autosigs route after ne...
Definition: TrainUnit.cpp:17538
TTrainController::TrainVectorAt
TTrain & TrainVectorAt(int Caller, int VecPos)
Return a reference to the train at position VecPos in the TrainVector, carries out range checking on ...
Definition: TrainUnit.cpp:14387
TTrain::FrontElementSpeedLimit
int FrontElementSpeedLimit
Definition: TrainUnit.h:406
TTrack::NumberOfPlatforms
int NumberOfPlatforms(int Caller, AnsiString LocationName)
Returns the number of separate platforms (not platform elements) at a given location,...
Definition: TrackUnit.cpp:10286
TTrack::TSigElement::SpeedTag
int SpeedTag
the TrackElement SpeedTag value - specifies the signal element
Definition: TrackUnit.h:623
TTrain::DeleteTrain
void DeleteTrain(int Caller)
This is a housekeeping function to delete train heap objects (bitmaps) explicitly rather than by usin...
Definition: TrainUnit.cpp:215
TTrainController::NumFailures
int NumFailures
Definition: TrainUnit.h:780
TTrain::ExitSpeedFull
double ExitSpeedFull
speed when leaving the next element
Definition: TrainUnit.h:381
TTrain::SetHeadCodeGraphics
void SetHeadCodeGraphics(int Caller, AnsiString Code)
Set the four HeadCodeGrPtr[4] pointers to the appropriate character graphics with the current backgro...
Definition: TrainUnit.cpp:2259
TAllRoutes::SetTrailingSignalsOnAutoSigsRoute
void SetTrailingSignalsOnAutoSigsRoute(int Caller, int TrackVectorPosition, int XLinkPos)
Enter with signal at TrackVectorElement already set to red by the passing train.
Definition: TrackUnit.cpp:17191
clB5G5R5
#define clB5G5R5
Definition: GraphicUnit.h:286
TUtilities::TimeStamp
AnsiString TimeStamp()
creates a string of the form 'hh:mm:ss' for use in call & event logging
Definition: Utilities.cpp:73
TTrainController::RandomFailureCounter
unsigned int RandomFailureCounter
new at v2.4.0, resets after 53 seconds (53 prime so can trigger at any clock time)
Definition: TrainUnit.h:795
TAllRoutes::TLockedRouteClass::RouteNumber
int RouteNumber
the vector position number of the relevant route in AllRoutesVector
Definition: TrackUnit.h:1502
TTrainController::AddTrain
bool AddTrain(int Caller, int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass, double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr, int RepeatNumber, int IncrementalMinutes, int IncrementalDigits, int SignallerSpeed, bool SignallerControl, TActionEventType &EventType)
Introduce a new train to the railway, with the characteristics specified, returns true for success,...
Definition: TrainUnit.cpp:8340
TTrainController::~TTrainController
~TTrainController()
Destructor.
Definition: TrainUnit.cpp:7958
TTrainController::ControllerCheckNewServiceDepartureTime
AnsiString ControllerCheckNewServiceDepartureTime(int Caller, TActionVectorIterator Ptr, int RptNum, TTrainDataEntry *TDEPtr, TTrainDataEntry *LinkedTrainDataPtr, int IncrementalMinutes, AnsiString RetStr)
Similar to TTrain::CheckNewServiceDepartureTime for use in ContinuationEntryFloatingTTString.
Definition: TrainUnit.cpp:8837
TUtilities::CheckAndReadFileInt
bool CheckAndReadFileInt(std::ifstream &InFile, int Lowest, int Highest, int &OutInt)
checks that the value is an int lying between Lowest & Highest (inclusive), returns true for success ...
Definition: Utilities.cpp:251
TRailGraphics::Code7
Graphics::TBitmap * Code7
Definition: GraphicUnit.h:959
TTrackElement::ActiveTrackElementName
AnsiString ActiveTrackElementName
Location name used either in the timetable or for a continuation (continuation names not used in time...
Definition: TrackUnit.h:126
TRailGraphics::bm94set
Graphics::TBitmap * bm94set
Definition: GraphicUnit.h:515
TTrainController::LogEvent
void LogEvent(AnsiString Str)
store Str to the event log - moved from TUtilities for v0.6 so can record the tt clock value
Definition: TrainUnit.cpp:7969
TRailGraphics::CodeO
Graphics::TBitmap * CodeO
Definition: GraphicUnit.h:976
TAllRoutes::SignallerRemovedTrainAutoRoute
TOneRoute SignallerRemovedTrainAutoRoute
if train was on an AutoSigsRoute when removed then this stores the route so that signals can be reset
Definition: TrackUnit.h:1588
TTrainController::MassHigh
bool MassHigh
Definition: TrainUnit.h:743
TTrain::PlotTrainWithNewBackgroundColour
void PlotTrainWithNewBackgroundColour(int Caller, TColor NewBackgroundColour, TDisplay *Disp)
Changes the train's background colour (e.g. to pale green if stopped at a station) Note that this use...
Definition: TrainUnit.cpp:3263
TUtilities::SaveFileString
void SaveFileString(std::ofstream &OutFile, AnsiString SaveString)
stores the string value to the file, then a '0' delimiter then a CR
Definition: Utilities.cpp:131
TTrain::PlotAlternativeTrackRouteGraphic
void PlotAlternativeTrackRouteGraphic(int Caller, unsigned int LagElement, int LagELinkPos, int HOffset, int VOffset, TStraddle StraddleValue)
When a train moves off a bridge the other track may contain a route or have a train on it that has be...
Definition: TrainUnit.cpp:2976
TTrain::ExitTimeFull
TDateTime ExitTimeFull
times used in SetTrainMovementValues corresponding to the next element the train runs on
Definition: TrainUnit.h:414
TTrack::GapFlashRed
TGraphicElement * GapFlashRed
the red & green circle graphics used to show where the gaps are
Definition: TrackUnit.h:693
TTrainController::CheckAndPopulateListOfIDs
bool CheckAndPopulateListOfIDs(int Caller, AnsiString IDSet, TExitList &ExitList, bool GiveMessages)
Used to compile ExitList from a string list of element IDs, returns true for success or gives a messa...
Definition: TrainUnit.cpp:10249
LocTypeForRepeatEntry
@ LocTypeForRepeatEntry
Definition: TrainUnit.h:67
SignallerChangeDirection
@ SignallerChangeDirection
Definition: TrainUnit.h:51
TAllRoutes::TLockedRouteClass
Handles routes that are locked because of approaching trains.
Definition: TrackUnit.h:1500
TTrain::TrainAtLocation
bool TrainAtLocation(int Caller, AnsiString &LocationName)
True when the train is stopped at a timetabled location.
Definition: TrainUnit.cpp:7583
TTrain::IsTrainIDOnBridgeTrackPos01
bool IsTrainIDOnBridgeTrackPos01(int Caller, unsigned int TrackVectorPosition)
True if train is on a bridge on trackpos 0 & 1.
Definition: TrainUnit.cpp:2849
TActionVectorEntry
Contains a single train action in a timetable - repeat entry is also of this class though no train ac...
Definition: TrainUnit.h:94
TTrainController::CheckSessionContinuationAutoSigEntries
bool CheckSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
Part of the session file integrity check for ContinuationAutoSigEntries, true for success.
Definition: TrainUnit.cpp:14233
TOneCompleteFormattedTrain::OneFormattedTrainVector
TOneFormattedTrainVector OneFormattedTrainVector
Definition: TrainUnit.h:236
TRailGraphics::Code1
Graphics::TBitmap * Code1
Definition: GraphicUnit.h:953
TTrainController::TimetableMessage
void TimetableMessage(bool GiveMessages, AnsiString Message)
Sends a message to the user if GiveMessages is true, including ServiceReference (see above) if not nu...
Definition: TrainUnit.cpp:13618
TDisplay
Definition: DisplayUnit.h:48
TTrackElement::ConnLinkPos
int ConnLinkPos[4]
Connecting element link position (i.e. array positions of the connecting element links,...
Definition: TrackUnit.h:143
TTrainController::ContinuationAutoSigVector
TContinuationAutoSigVector ContinuationAutoSigVector
vector for TContinuationAutoSigEntry objects
Definition: TrainUnit.h:797
TTrain::StoppedWithoutPower
bool StoppedWithoutPower
Definition: TrainUnit.h:433
TTrain::NameInTimetableBeforeCDT
int NameInTimetableBeforeCDT(int Caller, AnsiString Name, bool &Stop)
Returns the number by which the train ActionVectorEntryPtr needs to be incremented to point to the lo...
Definition: TrainUnit.cpp:4288
TTrainFormattedInformation::Header
AnsiString Header
description, mass, power, brake rate etc
Definition: TrainUnit.h:248
TActionEventType
TActionEventType
Used for reporting error conditions & warnings.
Definition: TrainUnit.h:39
TTrainController::TrainExistsAtIdent
bool TrainExistsAtIdent(int Caller, int TrainID)
new at v2.4.0 return true if find the train (added at v2.4.0 as can select a removed train in OAListB...
Definition: TrainUnit.cpp:8669
SignallerJoin
@ SignallerJoin
Definition: TrainUnit.h:50
TTrain::RearStartElement
int RearStartElement
start TrackVectorPosition element for rear of train
Definition: TrainUnit.h:317
TTrain::StepForwardFlag
bool StepForwardFlag
set when the signaller command to step forward one element has been given
Definition: TrainUnit.h:365
TTrainController::SplitTrainInfo
bool SplitTrainInfo(int Caller, AnsiString TrainInfoStr, AnsiString &HeadCode, AnsiString &Description, int &StartSpeed, int &MaxRunningSpeed, int &Mass, double &MaxBrakeRate, double &PowerAtRail, int &SignallerSpeed, bool GiveMessages)
Parse a train information entry, return true for success; PowerAtRail changed to double& from int& at...
Definition: TrainUnit.cpp:10332
TFixedTrackPiece::Config
TConfiguration Config[4]
the type of link - see TConfiguration above
Definition: TrackUnit.h:95
TTrainController::Operate
void Operate(int Caller)
called every clock tick to introduce new trains and update existing trains
Definition: TrainUnit.cpp:7981
TTrackElement::TrainIDOnElement
int TrainIDOnElement
Definition: TrackUnit.h:151
TRailGraphics::smRed
Graphics::TBitmap * smRed
Definition: GraphicUnit.h:885
TTrain::PowerAtRail
double PowerAtRail
in Watts (taken as 80% of the train's Gross Power, i.e. that entered by the user)
Definition: TrainUnit.h:396
TRailGraphics::LCPlainMan
Graphics::TBitmap * LCPlainMan
Definition: GraphicUnit.h:730
TDisplay::GetOutputLog4
TLabel * GetOutputLog4()
Definition: DisplayUnit.h:159
TAllRoutes::RebuildRailwayFlag
bool RebuildRailwayFlag
this is set whenever a route has to be cancelled forcibly in order to force a ClearandRebuildRailway ...
Definition: TrackUnit.h:1569
TTrainDataEntry::MaxRunningSpeed
double MaxRunningSpeed
in km/h
Definition: TrainUnit.h:183
TUtilities::LoadFileInt
int LoadFileInt(std::ifstream &InFile)
loads an int value from the file
Definition: Utilities.cpp:154
TTrackElement::ElementID
AnsiString ElementID
the element identifier based on position in the railway
Definition: TrackUnit.h:128
TRailGraphics::Code6
Graphics::TBitmap * Code6
Definition: GraphicUnit.h:958
TTrainDataEntry::TrainOperatingDataVector
TTrainOperatingDataVector TrainOperatingDataVector
operating information for the train including all its repeats
Definition: TrainUnit.h:197
TTrackElement::VLoc
int VLoc
The h & v locations in the railway (top lh corner of the first build screen = 0,0)
Definition: TrackUnit.h:145
TTrainController::OperatingTrainLateMins
float OperatingTrainLateMins
total late minutes of operating trains on exit operation for locations not reached yet
Definition: TrainUnit.h:751
TTrack::GetNonPointsOppositeLinkPos
int GetNonPointsOppositeLinkPos(int LinkPosIn)
Return the corresponding link position (track always occupies either links 0 & 1 or 2 & 3)
Definition: TrackUnit.h:791
TTrain::UnplotTrain
void UnplotTrain(int Caller)
Unplot train from screen in zoomed-in mode.
Definition: TrainUnit.cpp:518
TTrain::IsThereAnAdjacentTrain
bool IsThereAnAdjacentTrain(int Caller, TTrain *&TrainToBeJoinedBy)
Definition: TrainUnit.cpp:4712
TActionVectorEntry::ExitList
TExitList ExitList
the list of valid train exit TrackVector positions for 'Fer' entries (empty to begin with)
Definition: TrainUnit.h:108
TTrain::TRSTime
TDateTime TRSTime
location departure time and 'train ready to start' time (TRSTime is 10 seconds before the ReleaseTime...
Definition: TrainUnit.h:416
TAllRoutes::TLockedRouteClass::LastTrackVectorPosition
unsigned int LastTrackVectorPosition
the TrackVector position of the last (i.e. most forward) element in the route
Definition: TrackUnit.h:1506
FailMissedJBO
@ FailMissedJBO
Definition: TrainUnit.h:41
TUtilities::EventLog
std::deque< AnsiString > EventLog
event store, saved to the errorlog for diagnostic purposes
Definition: Utilities.h:57
TAllRoutes::NoRoute
@ NoRoute
Definition: TrackUnit.h:1516
TActionVectorEntry::FormatType
TTimetableFormatType FormatType
defines the timetable action type
Definition: TrainUnit.h:110
TTrain::RemainHereLogNotSent
bool RemainHereLogNotSent
flag to prevent repeated logs, new at v1.2.0
Definition: TrainUnit.h:296
TRailGraphics::CodeU
Graphics::TBitmap * CodeU
Definition: GraphicUnit.h:982
TTrainController::RestartTime
TDateTime RestartTime
TTClockTime when operation pauses ( = timetable start time prior to operation) TTClockTime is calcula...
Definition: TrainUnit.h:644
TTrain::NewShuttleFromNonRepeatService
void NewShuttleFromNonRepeatService(int Caller)
Carry out the actions needed when a new shuttle service is created from a non-repeating (F-nshs) serv...
Definition: TrainUnit.cpp:6089
TTrainController::TimetableStartTime
TDateTime TimetableStartTime
the start time of the current timetable
Definition: TrainUnit.h:642
clSPADBackground
#define clSPADBackground
Definition: GraphicUnit.h:300
TTrainController::UnplotTrains
void UnplotTrains(int Caller)
unplot all trains from screen
Definition: TrainUnit.cpp:8324
TRailGraphics::smBrightGreen
Graphics::TBitmap * smBrightGreen
Definition: GraphicUnit.h:877
TTrainController::EarlyArrivals
int EarlyArrivals
Definition: TrainUnit.h:765
FailCreateTrain
@ FailCreateTrain
Definition: TrainUnit.h:40
TTrain::IncrementalDigits
int IncrementalDigits
the number of digits to increment by in repeat entries
Definition: TrainUnit.h:313
TTrainController::CreateFormattedTimetable
void CreateFormattedTimetable(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir)
Examines the internal timetable (TrainDataVector) and creates from it a chronological (....
Definition: TrainUnit.cpp:14400
TTrack::RouteFlashFlag
bool RouteFlashFlag
true while a route is flashing prior to being set
Definition: TrackUnit.h:660
TRailGraphics::Code_v
Graphics::TBitmap * Code_v
Definition: GraphicUnit.h:947
TTrackElement::StationEntryStopLinkPos1
int StationEntryStopLinkPos1
Definition: TrackUnit.h:149
Points
@ Points
Definition: TrackUnit.h:63
TRailGraphics::LCPlain
Graphics::TBitmap * LCPlain
Definition: GraphicUnit.h:723
TTrain::LastActionDelayFlag
bool LastActionDelayFlag
used when trains join to ensure that there is a 30 second delay before the actual join takes place af...
Definition: TrainUnit.h:353
TTrainController::TotLatePassMins
float TotLatePassMins
Definition: TrainUnit.h:760
TTrainController::StripSpaces
void StripSpaces(int Caller, AnsiString &Input)
Strip both leading and trailing spaces at ends of Input and spaces before and after all commas and se...
Definition: TrainUnit.cpp:12797
FailMissedJoinOther
@ FailMissedJoinOther
Definition: TrainUnit.h:41
TTrain::RepeatShuttleOrRemainHere
void RepeatShuttleOrRemainHere(int Caller)
Carry out the actions needed to create either a new shuttle service or (if all repeats have finished)...
Definition: TrainUnit.cpp:6128
TTrain::JoinedOtherTrainFlag
bool JoinedOtherTrainFlag
true when the train has joined another train following an 'Fjo' timetable command or a signaller join...
Definition: TrainUnit.h:351
TTrainController::BFLow
bool BFLow
Definition: TrainUnit.h:743
TTrainController::TotArrDepPass
int TotArrDepPass
Definition: TrainUnit.h:778
SignallerPassRedSignal
@ SignallerPassRedSignal
Definition: TrainUnit.h:51
TRailGraphics::ChangeForegroundColour
void ChangeForegroundColour(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewForegroundColour, TColor BackgroundColour)
Definition: GraphicUnit.cpp:3326
TRailGraphics::CodeE
Graphics::TBitmap * CodeE
Definition: GraphicUnit.h:966
FailLockedRoute
@ FailLockedRoute
Definition: TrainUnit.h:40
TTrain::SignallerRemoved
bool SignallerRemoved
set when removed under signaller control to force a removal from the display at the next clock tick
Definition: TrainUnit.h:359
clB5G3R0
#define clB5G3R0
Definition: GraphicUnit.h:267
TTrain::FrontCodePtr
Graphics::TBitmap * FrontCodePtr
points to the front headcode segment, this is set to red or blue depending on TrainMode
Definition: TrainUnit.h:450
TOneRoute::SetRouteSignals
void SetRouteSignals(int Caller) const
Called when setting a route to set all points appropriately. Also called when a new train is added at...
Definition: TrackUnit.cpp:15486
Continuation
@ Continuation
Definition: TrackUnit.h:63
TTrainController::CallOnWarning
bool CallOnWarning
Definition: TrainUnit.h:729
TTrack::GetFilletGraphic
Graphics::TBitmap * GetFilletGraphic(int Caller, TTrackElement TrackElement)
Return a pointer to the point fillet (the bit that appears to move when points are changed) for the p...
Definition: TrackUnit.cpp:6818
GraphicUnit.h
TTrack::PointFlashFlag
bool PointFlashFlag
true when points are flashing during manual change
Definition: TrackUnit.h:658
TTrainController::OnTimeDeps
int OnTimeDeps
Definition: TrainUnit.h:773
TTrain::RestoreTimetableLocation
AnsiString RestoreTimetableLocation
stores the location name at which signaller control is taken, to ensure that it is back at that locat...
Definition: TrainUnit.h:424
TTrainController::TimetableIntegrityCheck
bool TimetableIntegrityCheck(int Caller, char *FileName, bool GiveMessages, bool CheckLocationsExistInRailway)
Checks overall timetable integrity, calls many other specific checking functions, returns true for su...
Definition: TrainUnit.cpp:9016
AllRoutes
TAllRoutes * AllRoutes
the object pointer, object created in InterfaceUnit
Definition: TrackUnit.cpp:54
TTrain::SignallerMaxSpeed
int SignallerMaxSpeed
maximum train speed under signaller control (in km/h)
Definition: TrainUnit.h:323
NoEvent
@ NoEvent
Definition: TrainUnit.h:40
FailSplitDueToOtherTrain
@ FailSplitDueToOtherTrain
Definition: TrainUnit.h:40
TTrainController::SendPerformanceSummary
void SendPerformanceSummary(int Caller, std::ofstream &PerfFile)
At the end of operation a summary of overall performance is sent to the performance file by this func...
Definition: TrainUnit.cpp:16968
TTrain::ResetTrainElementID
void ResetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
After a train has moved off an element that element has its TrainIDOnElement value set back to -1 to ...
Definition: TrainUnit.cpp:2940
TRailGraphics::smMagenta
Graphics::TBitmap * smMagenta
Definition: GraphicUnit.h:881
RepairFailedTrain
@ RepairFailedTrain
Definition: TrainUnit.h:51
TTrainController::AllServiceCallingLocsMap
TAllServiceCallingLocsMap AllServiceCallingLocsMap
Definition: TrainUnit.h:712
TRailGraphics::smPaleGreen
Graphics::TBitmap * smPaleGreen
Definition: GraphicUnit.h:884
TRailGraphics::CodeS
Graphics::TBitmap * CodeS
Definition: GraphicUnit.h:980
TTrainController::ContinuationEntryFloatingTTString
AnsiString ContinuationEntryFloatingTTString(int Caller, TTrainDataEntry *TTDEPtr, int RepeatNumber, int IncrementalMinutes, int IncrementalDigits)
Build string for use in floating window for expected trains at continuations.
Definition: TrainUnit.cpp:8700
TFixedTrackPiece::SpeedTag
int SpeedTag
The element identification number - corresponds to the relevant SpeedButton->Tag.
Definition: TrackUnit.h:85
TTrain::SPADFlag
bool SPADFlag
set when running past a red signal without permission flags to indicate relevant stop conditions or p...
Definition: TrainUnit.h:430
TTrain::Stopped
bool Stopped()
True if the train has stopped for any reason.
Definition: TrainUnit.h:614
TTrack::SigTableGroundSignal
TSigElement SigTableGroundSignal[40]
new at version 0.6 for ground signals
Definition: TrackUnit.h:637
TTrain::TrainToJoinIsAdjacent
bool TrainToJoinIsAdjacent(int Caller, TTrain *&TrainToJoin)
True for a train waiting to join another when the other train is adjacent.
Definition: TrainUnit.cpp:6032
TTrain::OldZoomOutElement
int OldZoomOutElement[3]
stores the Lead, Mid & Lag TrackVectorPositions, used for unplotting trains from the old position in ...
Definition: TrainUnit.h:437
TPrefDirElement::GetELink
int GetELink() const
Returns ELink.
Definition: TrackUnit.h:271
TDisplay::GetOutputLog8
TLabel * GetOutputLog8()
Definition: DisplayUnit.h:179
WaitingForJBO
@ WaitingForJBO
Definition: TrainUnit.h:43
TUtilities::CheckFileDouble
bool CheckFileDouble(std::ifstream &InFile)
checks that the value is a double, returns true for success
Definition: Utilities.cpp:294
TTrain::Straddle
TStraddle Straddle
the current Straddle value of the train (see TStraddle above)
Definition: TrainUnit.h:457
SignallerStop
@ SignallerStop
Definition: TrainUnit.h:51
TRailGraphics::CodeB
Graphics::TBitmap * CodeB
Definition: GraphicUnit.h:963
TAllRoutes::DiagonalFouledByRouteOrTrain
bool DiagonalFouledByRouteOrTrain(int Caller, int HLoc, int VLoc, int DiagonalLinkNumber)
The track geometry allows diagonals to cross without occupying the same track element,...
Definition: TrackUnit.cpp:17819
TTrackElement::TempTrackMarker01
bool TempTrackMarker01
Definition: TrackUnit.h:137
Display
TDisplay * Display
The object pointer for the on-screen display, object created in InterfaceUnit.
Definition: DisplayUnit.cpp:53
TTrack::GetVLocMin
int GetVLocMin()
Definition: TrackUnit.h:785
TRailGraphics::Code3
Graphics::TBitmap * Code3
Definition: GraphicUnit.h:955
TUtilities::CheckFileBool
bool CheckFileBool(std::ifstream &InFile)
checks that the value is a bool returns true for success
Definition: Utilities.cpp:198
TTrack::OtherTrainOnTrack
bool OtherTrainOnTrack(int Caller, int NextPos, int NextEntryPos, int OwnTrainID)
True if another train on NextEntryPos track of element at NextPos, whether bridge or not,...
Definition: TrackUnit.cpp:9929
TTrain::MaximumPowerLimit
static const int MaximumPowerLimit
Watts (i.e. 100MW)
Definition: TrainUnit.h:282
TTrain::PlotStartPosition
void PlotStartPosition(int Caller)
Plots the train and sets up all relevant members for a new train when it is introduced into the railw...
Definition: TrainUnit.cpp:259
TTrack::TActiveTrackElementNameMapEntry
std::pair< AnsiString, int > TActiveTrackElementNameMapEntry
Definition: TrackUnit.h:618
TTrainController::TContinuationAutoSigEntry::SecondDelay
double SecondDelay
Definition: TrainUnit.h:653
TTrain::UnplotTrainInZoomOutMode
void UnplotTrainInZoomOutMode(int Caller)
Unplot train from screen in zoomed-out mode.
Definition: TrainUnit.cpp:7546
TTimetableFormatType
TTimetableFormatType
Timetable entry types.
Definition: TrainUnit.h:61
TTrainController::TrainFailedWarning
bool TrainFailedWarning
Flags to enable the relevant warning graphics to flash at the left hand side of the screen.
Definition: TrainUnit.h:729
TTrain::NextTrainID
static int NextTrainID
the ID value to be used for the next train that is created, static so that it doesn't need an object ...
Definition: TrainUnit.h:287
TTrainController::TLocServiceTimes
Class used for timetable conflict file compilation.
Definition: TrainUnit.h:698
TTrainOperatingData::RunningEntry
TRunningEntry RunningEntry
Definition: TrainUnit.h:157
NotAShuttleLink
@ NotAShuttleLink
Definition: TrainUnit.h:75
TRailGraphics::gl91set
Graphics::TBitmap * gl91set
Definition: GraphicUnit.h:706
TRailGraphics::Code_y
Graphics::TBitmap * Code_y
Definition: GraphicUnit.h:950
TAllRoutes::CheckMapAndRoutes
void CheckMapAndRoutes(int Caller)
Diagnostic function - checks equivalence for each route between entries in PrefDirVector and those in...
Definition: TrackUnit.cpp:16967
TAllRoutes::GetRouteElementDataFromRoute2MultiMap
TRouteElementPair GetRouteElementDataFromRoute2MultiMap(int Caller, int HLoc, int VLoc, TRouteElementPair &SecondPair)
Retrieve up to two TRouteElementPair entries from Route2MultiMap at H & V, the first as a function re...
Definition: TrackUnit.cpp:16923
TTrainController::GetExitLocationAndAt
AnsiString GetExitLocationAndAt(int Caller, TExitList &ExitList) const
Check all timetable names in ExitList, if all same return " at [name]" else return ""....
Definition: TrainUnit.cpp:16900
TTrainController::TrainAdded
bool TrainAdded
true when a train has been added by a split (occurs outside the normal train introduction process)
Definition: TrainUnit.h:733
TActionVectorEntry::LocationName
AnsiString LocationName
Definition: TrainUnit.h:96
TTrainFormattedInformation::OneCompleteFormattedTrainVector
TOneCompleteFormattedTrainVector OneCompleteFormattedTrainVector
Definition: TrainUnit.h:252
ShuttleFinishedRemainingHere
@ ShuttleFinishedRemainingHere
Definition: TrainUnit.h:43
FailUnexpectedBuffers
@ FailUnexpectedBuffers
Definition: TrainUnit.h:41
Start
@ Start
Definition: TrainUnit.h:71
TRailGraphics::TempBackground
Graphics::TBitmap * TempBackground
Definition: GraphicUnit.h:888
TTrain::TimeTimeLocArrived
bool TimeTimeLocArrived
indicates whether has arrived (true) or not when ActionVectorEntryPtr->FormatType == TimeTimeLoc
Definition: TrainUnit.h:294
TActionType
TActionType
Used in LogAction when reporting a train action to the performance log & file.
Definition: TrainUnit.h:48
TTrainController::TContinuationAutoSigEntry::ThirdDelay
double ThirdDelay
Delays in seconds before consecutive signal changes - these correspond to the times taken for trains ...
Definition: TrainUnit.h:653
TTrain::LeadEntryPos
int LeadEntryPos
Definition: TrainUnit.h:327
TTrainController::LateArrivals
int LateArrivals
Definition: TrainUnit.h:768
TRailGraphics::CodeZ
Graphics::TBitmap * CodeZ
Definition: GraphicUnit.h:987
NoShuttleLink
@ NoShuttleLink
Definition: TrainUnit.h:75
TTrainController::ReplotTrains
void ReplotTrains(int Caller, TDisplay *Disp)
plot all trains on the display
Definition: TrainUnit.cpp:8294
TTrainController::CheckShuttleServiceIntegrity
bool CheckShuttleServiceIntegrity(int Caller, TTrainDataEntry *TDEntryPtr, bool GiveMessages)
Check that each shuttle service ends either in Fns or Fxx-sh (though a single service can't end in Fx...
Definition: TrainUnit.cpp:13565
TTrain::FloatingTimetableString
AnsiString FloatingTimetableString(int Caller, TActionVectorEntry *Ptr)
Used in the floating window to display the timetable.
Definition: TrainUnit.cpp:6590
TAllRoutes::GetModifiableRouteAt
TOneRoute & GetModifiableRouteAt(int Caller, int At)
Returns a modifiable reference to the route at AllRoutesVector position 'At', after performing range ...
Definition: TrackUnit.cpp:16190
TTrain::AValue
double AValue
this is a useful shorthand value in calculating speeds and transit times in SetTrainMovementValues [=...
Definition: TrainUnit.h:373
FailUnexpectedExitRailway
@ FailUnexpectedExitRailway
Definition: TrainUnit.h:41
TTrain::FrontTrainSplit
void FrontTrainSplit(int Caller)
Carry out the actions needed when a train is to split from the front.
Definition: TrainUnit.cpp:5066
TTrainController::WithinTimeRange
bool WithinTimeRange(int Caller, AnsiString Time1, AnsiString Time2, int MinuteRange)
check whether the two times are within the range in minutes specified and return true if so....
Definition: TrainUnit.cpp:16299
TTrainController::WriteTrainsToImage
void WriteTrainsToImage(int Caller, Graphics::TBitmap *Bitmap)
Called by TInterface::SaveOperatingImage1Click) to write all trains to the image file.
Definition: TrainUnit.cpp:8309
TActionVectorEntry::LinkedTrainEntryPtr
TTrainDataEntry * LinkedTrainEntryPtr
link pointer for use between fsp/rsp & Sfs; Fjo & jbo; Fns & Sns; & all shuttle to shuttle links
Definition: TrainUnit.h:118
TTrainController::AtLocSuccessor
bool AtLocSuccessor(const TActionVectorEntry &AVEntry)
A shorthand function that returns true if the successor to a given timetable action command should be...
Definition: TrainUnit.cpp:12229
TTrain::ZeroPowerNoJoinedByMessage
bool ZeroPowerNoJoinedByMessage
Definition: TrainUnit.h:303
TTrain::CalcTimeToAct
float CalcTimeToAct(int Caller)
new v2.2.0 for operator action panel. Calculates the time left for operator action to avoid unnecessa...
Definition: TrainUnit.cpp:7690
TTrainController::SaveSessionContinuationAutoSigEntries
void SaveSessionContinuationAutoSigEntries(int Caller, std::ofstream &SessionFile)
save ContinuationAutoSigEntries to a session file
Definition: TrainUnit.cpp:14193
TTrainOperatingData::TrainID
int TrainID
Definition: TrainUnit.h:155
TDisplay::GetOutputLog3
TLabel * GetOutputLog3()
Definition: DisplayUnit.h:154
NoLocation
@ NoLocation
Definition: TrainUnit.h:67
TRailGraphics::bmTransparentBgnd
Graphics::TBitmap * bmTransparentBgnd
Definition: GraphicUnit.h:898
TTrain::FirstHalfMove
bool FirstHalfMove
true when the train is on the first half of an element when it displays as fully on two elements....
Definition: TrainUnit.h:349
FailLocTooShort
@ FailLocTooShort
Definition: TrainUnit.h:40
TTrainDataEntry::Mass
int Mass
in kg
Definition: TrainUnit.h:187
NoMode
@ NoMode
Definition: TrainUnit.h:56
TTrainController::LatePasses
int LatePasses
Definition: TrainUnit.h:770
TTrainController::SplitRepeat
bool SplitRepeat(int Caller, AnsiString OneEntry, int &RearStartOrRepeatMins, int &FrontStartOrRepeatDigits, int &RepeatNumber, bool GiveMessages)
Parse a timetable repeat entry, return true for success.
Definition: TrainUnit.cpp:10678
TFixedTrackPiece::TrackType
TTrackType TrackType
the type of track element
Definition: TrackUnit.h:98
TTrainController::TLocServiceTimes::ArrTime
AnsiString ArrTime
Definition: TrainUnit.h:702
TTrainController::SSHigh
bool SSHigh
Definition: TrainUnit.h:743
TimeLoc
@ TimeLoc
Definition: TrainUnit.h:62
TActionVectorIterator
TActionVector::iterator TActionVectorIterator
iterator
Definition: TrainUnit.h:146
TPrefDirElement
Basic preferred direction or route element - track element with additional members.
Definition: TrackUnit.h:210
TTrainController::AvHoursIntValue
int AvHoursIntValue
Input in MTBFEditBox in timetable hours, min value is 1 and max is 10,000. Here because performance f...
Definition: TrainUnit.h:788
TTrainController::CrashedTrains
int CrashedTrains
Definition: TrainUnit.h:763
TTrainController::TTEditPanelVisible
bool TTEditPanelVisible
new at v2.6.0 so potential error message only shows in TTEdit mode
Definition: TrainUnit.h:741
TDisplay::WarningLog
void WarningLog(int Caller, AnsiString Statement)
Display warning message Statement in the bottom left hand warning position and scroll other messages ...
Definition: DisplayUnit.cpp:457
TTrain::LogAction
void LogAction(int Caller, AnsiString HeadCode, AnsiString OtherHeadCode, TActionType ActionType, AnsiString LocationName, TDateTime TimetableNonRepeatTime, bool Warning)
Send a message to the performance log and performance file, and if the message is flagged as a warnin...
Definition: TrainUnit.cpp:4795
TTrainController::OperatingTrainArrDep
int OperatingTrainArrDep
< all these set to 0 in constructor
Definition: TrainUnit.h:782
TTrain::StoppedAtSignal
bool StoppedAtSignal
Definition: TrainUnit.h:432
TTrainController::GetControllerTrainTime
TDateTime GetControllerTrainTime(int Caller, TDateTime Time, int RepeatNumber, int IncrementalMinutes)
Get the interval between repeats.
Definition: TrainUnit.cpp:8688
TRailGraphics::Code_u
Graphics::TBitmap * Code_u
Definition: GraphicUnit.h:946
TTrackElement::Attribute
int Attribute
special variable used only for points, signals & level crossings, ignored otherwise; points 0=set to ...
Definition: TrackUnit.h:139
TTrainController::NotStartedTrainArrDep
int NotStartedTrainArrDep
total number of arrivals & departures for trains that haven't started yet for locations not reached y...
Definition: TrainUnit.h:784
TStraddle
TStraddle
Defines the train position with respect to the track elements; three consecutive elements are Lead (f...
Definition: TrainUnit.h:267
TTrainController::THCandTrainPosParam
std::pair< AnsiString, int > THCandTrainPosParam
Definition: TrainUnit.h:714
TTrainDataVector
std::vector< TTrainDataEntry > TTrainDataVector
vector class for containing the whole timetable (the object is a member of TTrainController)
Definition: TrainUnit.h:210
TTrainController::GetRepeatTime
TDateTime GetRepeatTime(int Caller, TDateTime BasicTime, int RepeatNumber, int IncMinutes)
Return the repeating service time.
Definition: TrainUnit.cpp:13252
clCrashedBackground
#define clCrashedBackground
Definition: GraphicUnit.h:293
TTrack::IsLCAtHV
bool IsLCAtHV(int Caller, int HLoc, int VLoc)
True if a level crossing is found at H & V.
Definition: TrackUnit.cpp:6650
TTrain::OpTimeToAct
float OpTimeToAct
in minutes: new at v2.2.0 for operator time to act panel. Calculated in UpdateTrain,...
Definition: TrainUnit.h:402
TTrainController::TTClockTime
TDateTime TTClockTime
the time indicated by the timetable clock
Definition: TrainUnit.h:640
TAllRoutes::TLockedRouteClass::LockStartTime
TDateTime LockStartTime
the timetable time at which the route is locked, to start the 2 minute clock
Definition: TrackUnit.h:1510
TActionVectorEntry::SequenceType
TTimetableSequenceType SequenceType
indicates where in the sequence of codes the action lies
Definition: TrainUnit.h:114
TTrain::RearStartExitPos
int RearStartExitPos
the LinkPos value for the rear starting element (i.e. links to the front starting element)
Definition: TrainUnit.h:319
TTrainController::TLocServiceTimes::FrhMarker
AnsiString FrhMarker
Definition: TrainUnit.h:704
TRailGraphics::Code_o
Graphics::TBitmap * Code_o
Definition: GraphicUnit.h:940
TRailGraphics::CodeV
Graphics::TBitmap * CodeV
Definition: GraphicUnit.h:983
TTrainController::TLocServiceTimes::ServiceAndRepeatNum
AnsiString ServiceAndRepeatNum
Definition: TrainUnit.h:700
TTrain::SetTrainElementID
void SetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
When a train moves onto an element that element has its TrainIDOnElement value set to the TrainID val...
Definition: TrainUnit.cpp:2907
TActionVectorEntry::NonRepeatingShuttleLinkHeadCode
AnsiString NonRepeatingShuttleLinkHeadCode
string values for timetabled action entries, null on creation
Definition: TrainUnit.h:96
TActionVectorEntry::ShuttleLinkType
TTimetableShuttleLinkType ShuttleLinkType
indicates whether or not the action relates to a shuttle service link
Definition: TrainUnit.h:116
TTrain::TimetableFinished
bool TimetableFinished
set when there are no more timetable actions
Definition: TrainUnit.h:369
TUtilities::CallLog
std::deque< AnsiString > CallLog
call stack store, saved to the errorlog for diagnostic purposes
Definition: Utilities.h:55
TRailGraphics::CodeN
Graphics::TBitmap * CodeN
Definition: GraphicUnit.h:975
TActionVectorEntry::Command
AnsiString Command
Definition: TrainUnit.h:96
TTrain::CallingOnAllowed
bool CallingOnAllowed(int Caller)
True if the train can be called on at its current position - see detail in .cpp file.
Definition: TrainUnit.cpp:4444
TTrainController::TContinuationTrainExpectationEntry::HeadCode
AnsiString HeadCode
service headcode
Definition: TrainUnit.h:673
TTrain::RepeatShuttleOrNewNonRepeatService
void RepeatShuttleOrNewNonRepeatService(int Caller)
Carry out the actions needed to create either a new shuttle service or (if all repeats have finished)...
Definition: TrainUnit.cpp:6188
TTrackElement::Length23
int Length23
Definition: TrackUnit.h:147
TTrain::SignallerStoppingFlag
bool SignallerStoppingFlag
set when the signaller stop command has been given
Definition: TrainUnit.h:361
TUtilities::LoadFileBool
bool LoadFileBool(std::ifstream &InFile)
loads a bool value from the file
Definition: Utilities.cpp:141
TTrainController::SigSLow
bool SigSLow
Message flags in TT checks to stop being given twice.
Definition: TrainUnit.h:743
TRailGraphics::Code_m
Graphics::TBitmap * Code_m
Definition: GraphicUnit.h:938
TTrack::AnyLinkedBarrierDownVectorManual
bool AnyLinkedBarrierDownVectorManual(int Caller, int HLoc, int VLoc, int &BDVectorPos)
Checks BarrierDownVector and returns true if there is one that is linked to the LC at H & V positions...
Definition: TrackUnit.cpp:5689
TRailGraphics::gl92set
Graphics::TBitmap * gl92set
Definition: GraphicUnit.h:708
SNSShuttle
@ SNSShuttle
Definition: TrainUnit.h:62
RemoveTrain
@ RemoveTrain
Definition: TrainUnit.h:50
TTrainController::TLocServiceTimes::DepTime
AnsiString DepTime
Definition: TrainUnit.h:703
TTrainController::SetWarningFlags
void SetWarningFlags(int Caller)
This sets all the warning flags (CrashWarning, DerailWarning etc) to their required states after a se...
Definition: TrainUnit.cpp:17236
TDisplay::PlotSmallOutput
void PlotSmallOutput(int Caller, int HPos, int VPos, Graphics::TBitmap *PlotItem)
Plot small (4x4) graphic PlotItem on the zoomed-out display at HPos & Vpos.
Definition: DisplayUnit.cpp:111
TTrainController::TContinuationTrainExpectationEntry
Class that stores data for trains expected at continuation entries (kept in a multimap - see below),...
Definition: TrainUnit.h:669
SNSNonRepeatFromShuttle
@ SNSNonRepeatFromShuttle
Definition: TrainUnit.h:62
TTrainController::TotEarlyPassMins
float TotEarlyPassMins
Definition: TrainUnit.h:757
TRailGraphics::CodeH
Graphics::TBitmap * CodeH
Definition: GraphicUnit.h:969
TTrainController::OpTimeToActMultiMap
TOpTimeToActMultiMap OpTimeToActMultiMap
added v2.2.0 for Op time to act display
Definition: TrainUnit.h:801
TTrainController::TContinuationAutoSigEntry::AccessNumber
int AccessNumber
the number of times the signal changing function has been accessed - starts at 0 and increments after...
Definition: TrainUnit.h:655
TActionVectorEntry::SignallerControl
bool SignallerControl
indicates a train that is defined by the timetable as under signaller control
Definition: TrainUnit.h:98
TTrain::GetTrainTime
TDateTime GetTrainTime(int Caller, TDateTime Time)
Returns the timetable action time corresponding to 'Time' for this train, i.e. it adjusts the time va...
Definition: TrainUnit.cpp:4701
TTrain::BrakeRate
double BrakeRate
the current train brake rate
Definition: TrainUnit.h:391
TTrackElement::SpeedLimit23
int SpeedLimit23
Element lengths and speed limits, ...01 is for the track with link positions [0] and [1],...
Definition: TrackUnit.h:147
TTrainController::TContinuationAutoSigEntry::PassoutTime
TDateTime PassoutTime
the timetable clock time at which the train exits from the continuation
Definition: TrainUnit.h:659
TTrain::FinishJoinLogSent
bool FinishJoinLogSent
Definition: TrainUnit.h:298
TTrack::GapFlashFlag
bool GapFlashFlag
true when a pair of connected gaps is flashing
Definition: TrackUnit.h:648
TRailGraphics::gl95set
Graphics::TBitmap * gl95set
Definition: GraphicUnit.h:712
TrainUnit.h
PassTime
@ PassTime
Definition: TrainUnit.h:63
TTrainController::IncorrectExits
int IncorrectExits
Definition: TrainUnit.h:767
TTrain::ContinuationExit
bool ContinuationExit(int Caller, int Element, int Exitpos) const
True if Element is a continuation and Exitpos is the continuation end.
Definition: TrainUnit.cpp:2831
TFixedTrackPiece::Link
int Link[4]
Track connection link values, max. of 4, unused = -1, top lh diag.=1, top=2, top rh diag....
Definition: TrackUnit.h:87
TTrain::UpdateTrain
void UpdateTrain(int Caller)
Major function called at each clock tick for each train & handles all train movement & associated act...
Definition: TrainUnit.cpp:604
TRailGraphics::LockedRouteCancelPtr
Graphics::TBitmap * LockedRouteCancelPtr[10]
for locked route cancel graphic, 1 for each of 8 links, 0 & 5 included as for direction
Definition: GraphicUnit.h:1036
TRailGraphics::Code8
Graphics::TBitmap * Code8
Definition: GraphicUnit.h:960
TTrainController::SameDirection
bool SameDirection(int Caller, AnsiString Ref1, AnsiString Ref2, AnsiString Time1, AnsiString Time2, int RepeatNum1, int RepeatNum2, TServiceCallingLocsList List1, TServiceCallingLocsList List2, AnsiString Location, bool Arrival)
Determines whether two services are running in the same direction when they arrive or depart from Loc...
Definition: TrainUnit.cpp:16582
TTrain::TrainToBeJoinedByIsAdjacent
bool TrainToBeJoinedByIsAdjacent(int Caller, TTrain *&TrainToBeJoinedBy)
True for a train waiting to be joined when the joining train is adjacent.
Definition: TrainUnit.cpp:6060
TTrain::SetTrainMovementValues
void SetTrainMovementValues(int Caller, int TrackVectorPosition, int EntryPos)
Calculates train speeds and times for the element that the train is about to enter....
Definition: TrainUnit.cpp:3300
TActionVectorEntry::Warning
bool Warning
if set triggers an alert in the warning panel when the action is reached
Definition: TrainUnit.h:100
TTrain::PickUpBackgroundBitmap
void PickUpBackgroundBitmap(int Caller, int HOffset, int VOffset, int Element, int EntryPos, Graphics::TBitmap *GraphicPtr) const
Store the background bitmap pointer (BackgroundPtr - see above) prior to being overwritten by the tra...
Definition: TrainUnit.cpp:2439
clStoppedTrainInFront
#define clStoppedTrainInFront
Definition: GraphicUnit.h:302
TDisplay::GetOutputLog10
TLabel * GetOutputLog10()
Definition: DisplayUnit.h:189
TTrainDataEntry::ServiceReference
AnsiString ServiceReference
Definition: TrainUnit.h:179
TTrain::SignallerChangeTrainDirection
void SignallerChangeTrainDirection(int Caller)
Unplots & replots train, which checks for facing signal and sets StoppedAtSignal if req'd.
Definition: TrainUnit.cpp:6364
TTrain::ZeroPowerNoRearSplitMessage
bool ZeroPowerNoRearSplitMessage
Definition: TrainUnit.h:301
FailMissedChangeDirection
@ FailMissedChangeDirection
Definition: TrainUnit.h:42
NotSet
@ NotSet
Definition: TrackUnit.h:73
Repeat
@ Repeat
Definition: TrainUnit.h:63
TRailGraphics::Code_c
Graphics::TBitmap * Code_c
Definition: GraphicUnit.h:928
TTrainController::StripExcessFromHeadCode
void StripExcessFromHeadCode(int Caller, AnsiString &HeadCode)
change an extended headcode to an ordinary 4 character headcode
Definition: TrainUnit.cpp:12238
TTrack::TIMPair
std::pair< unsigned int, unsigned int > TIMPair
TrackElement pair type used for inactive elements, values are vector positions.
Definition: TrackUnit.h:586
TUtilities::Clock2Stopped
bool Clock2Stopped
when true the main loop - Interface->ClockTimer2 - is stopped
Definition: Utilities.h:38
TTrainController::MRSHigh
bool MRSHigh
Definition: TrainUnit.h:743
TTrackElement::GroundSignal
@ GroundSignal
Definition: TrackUnit.h:156
LevelCrossing
@ LevelCrossing
Definition: TrackUnit.h:64
TDisplay::GetOutputLog2
TLabel * GetOutputLog2()
Definition: DisplayUnit.h:149
TTrain::NotInService
bool NotInService
Definition: TrainUnit.h:433
TRailGraphics::CodeA
Graphics::TBitmap * CodeA
Definition: GraphicUnit.h:962
TTrack::ActiveTrackElementNameMapCompiledFlag
bool ActiveTrackElementNameMapCompiledFlag
indicates that the ActiveTrackElementNameMap has been compiled
Definition: TrackUnit.h:642
TTrainController::LocServiceTimesLocationSort
bool LocServiceTimesLocationSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:812
TOneTrainFormattedEntry
A single train timetable action for use in a formatted timetable.
Definition: TrainUnit.h:218
TTrain::RearTrainSplit
void RearTrainSplit(int Caller)
Carry out the actions needed when a train is to split from the rear.
Definition: TrainUnit.cpp:5342
TTrainController::ConsolidateSARNTArrDep
AnsiString ConsolidateSARNTArrDep(int Caller, const AnsiString Input, int &NumTrainsAtLoc, AnsiString Location, bool Arrival, bool &AnalysisError, int &MaxNumberOfSameDirections)
Removes duplicates from and sorts ServiceAndRepeatNumTotal into alphabetical order for arrivals (bool...
Definition: TrainUnit.cpp:16325
Track
TTrack * Track
the object pointer, object created in InterfaceUnit
Definition: TrackUnit.cpp:53
TTrackElement::Conn
int Conn[4]
Connecting element position in TrackVector, set to -1 if no connecting link or if track not linked.
Definition: TrackUnit.h:141
Signaller
@ Signaller
Definition: TrainUnit.h:56
TTrain::TrainCrashedInto
int TrainCrashedInto
the TrainID of the train that this train has crashed into, recorded so that train can be marked and d...
Definition: TrainUnit.h:443
TRailGraphics::smYellow
Graphics::TBitmap * smYellow
Definition: GraphicUnit.h:886
RailGraphics
TRailGraphics * RailGraphics
the object pointer, object created in InterfaceUnit
Definition: GraphicUnit.cpp:50
TTrainOperatingData
Data for a specific train for use during operation.
Definition: TrainUnit.h:153
FailCreatePoints
@ FailCreatePoints
Definition: TrainUnit.h:40
TRailGraphics::smBlack
Graphics::TBitmap * smBlack
Definition: GraphicUnit.h:875
Bridge
@ Bridge
Definition: TrackUnit.h:63
TRailGraphics::Code_p
Graphics::TBitmap * Code_p
Definition: GraphicUnit.h:941
TTrainFormattedInformation::NumberOfTrains
int NumberOfTrains
number of repeats + 1
Definition: TrainUnit.h:250
TTrain::LagExitPos
int LagExitPos
TrackVector positions, & entry & exit connection positions for the elements that the train occupies.
Definition: TrainUnit.h:327
TTimetableSequenceType
TTimetableSequenceType
Definition: TrainUnit.h:70
TTrain::TimetableMaxRunningSpeed
double TimetableMaxRunningSpeed
the maximum train running speed when in timetable mode (see int SignallerMaxSpeed for signaller contr...
Definition: TrainUnit.h:383
SequTypeForRepeatEntry
@ SequTypeForRepeatEntry
Definition: TrainUnit.h:71
TRailGraphics::bmName
Graphics::TBitmap * bmName
Definition: GraphicUnit.h:523
clB0G0R0
#define clB0G0R0
Definition: GraphicUnit.h:36
Buffers
@ Buffers
Definition: TrackUnit.h:63
TActionVectorEntry::RearStartOrRepeatMins
int RearStartOrRepeatMins
Definition: TrainUnit.h:104
clFrontCodeTimetable
#define clFrontCodeTimetable
Definition: GraphicUnit.h:296
TActionVectorEntry::OtherHeadCode
AnsiString OtherHeadCode
Definition: TrainUnit.h:96